樹——線段樹

線段樹是一種二叉搜索樹,與區間樹相似,它將一個區間劃分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。

對於線段樹中的每一個非葉子節點[a,b],它的左兒子表示的區間爲[a,(a+b)/2],右兒子表示的區間爲[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最後的子節點數目爲N,即整個線段區間的長度。

使用線段樹可以快速的查找某一個節點在若干條線段中出現的次數,時間複雜度爲O(logN)。而未優化的空間複雜度爲2N,因此有時需要離散化讓空間壓縮。


案例:節點更新,查找最小值


#1077 : RMQ問題再臨-線段樹

時間限制:10000ms

單點時限:1000ms

內存限制:256MB


描述

上回說到:小Hi給小Ho出了這樣一道問題:假設整個貨架上從左到右擺放了N種商品,並且依次標號爲1到N,每次小Hi都給出一段區間[L, R],小Ho要做的是選出標號在這個區間內的所有商品重量最輕的一種,並且告訴小Hi這個商品的重量。但是在這個過程中,可能會因爲其他人的各種行爲,對某些位置上的商品的重量產生改變(如更換了其他種類的商品)。

小Ho提出了兩種非常簡單的方法,但是都不能完美的解決。那麼這一次,面對更大的數據規模,小Ho將如何是好呢?

提示:其實只是比ST少計算了一些區間而已

輸入

每個測試點(輸入文件)有且僅有一組測試數據。

每組測試數據的第1行爲一個整數N,意義如前文所述。

每組測試數據的第2行爲N個整數,分別描述每種商品的重量,其中第i個整數表示標號爲i的商品的重量weight_i。

每組測試數據的第3行爲一個整數Q,表示小Hi總共詢問的次數與商品的重量被更改的次數之和。

每組測試數據的第N+4~N+Q+3行,每行分別描述一次操作,每行的開頭均爲一個屬於0或1的數字,分別表示該行描述一個詢問和描述一次商品的重量的更改兩種情況。對於第N+i+3行,如果該行描述一個詢問,則接下來爲兩個整數Li, Ri,表示小Hi詢問的一個區間[Li, Ri];如果該行描述一次商品的重量的更改,則接下來爲兩個整數Pi,Wi,表示位置編號爲Pi的商品的重量變更爲Wi

對於100%的數據,滿足N<=10^6,Q<=10^6, 1<=Li<=Ri<=N,1<=Pi<=N, 0<weight_i, Wi<=10^4。

輸出

對於每組測試數據,對於每個小Hi的詢問,按照在輸入中出現的順序,各輸出一行,表示查詢的結果:標號在區間[Li, Ri]中的所有商品中重量最輕的商品的重量。

  • 樣例輸入

  • 10
    3655 5246 8991 5933 7474 7603 6098 6654 2414 884 
    6
    0 4 9
    0 2 10
    1 4 7009
    0 5 6
    1 3 7949
    1 3 1227
  • 樣例輸出

  • 2414
    884
    7474



AC代碼:


#include<iostream>

#include<cstdlib>

#include<algorithm>

#include<vector>

#include<list>

#include<iterator>

#include<string>

#include<stack>

using namespace std;

#define INF 0x3fffffff

const int MAX = 100000100;


struct NODE {

int value, left, right;

}node[MAX];


void BuildTree(int n, int left, int right) {

node[n].left = left;

node[n].right = right;

if (left == right)

{

scanf("%d",&node[n].value);

return;

}

int mid = (left + right) >> 1;

BuildTree(n << 1, left, mid);

BuildTree((n << 1) + 1, mid + 1, right);

node[n].value = min(node[n << 1].value, node[(n << 1) + 1].value);

}


int FindTree(int n, int begin, int end) {

int p1 = INF, p2 = INF;

if (node[n].left >= begin&&node[n].right <= end)

return node[n].value;

if (begin <= node[n << 1].right)

p1 = FindTree(n << 1, begin, end);

if (end >= node[(n << 1) + 1].left)

p2 = FindTree((n << 1) + 1, begin, end);

return min(p1, p2);

}


void UpdateTree(int n, int ind, int val) {

if (node[n].left == node[n].right)

{

node[n].value = val;

}

else

{

if (ind <= node[n << 1].right)

UpdateTree(n << 1, ind, val);

if (ind >= node[(n << 1) + 1].left)

UpdateTree((n << 1) + 1, ind, val);

node[n].value = min(node[n << 1].value, node[(n << 1) + 1].value);

}

}


int main()

{

int N;

int m;

int s, l, r;

while (~scanf("%d",&N))

{

BuildTree(1, 0, N - 1);

scanf("%d",&m);

for (int i = 0; i < m; i++)

{

scanf("%d %d %d", &s, &l, &r);

if (s == 0)

{

printf("%d\n",FindTree(1, l - 1, r - 1));

}

if (s == 1)

{

UpdateTree(1, l - 1, r);

}

}

}

return 0;

}



這道題AC的關鍵不是線段樹算法,而是標準輸入和輸出,如果用C++的cin和cout必然超時。

原因:

scanf是格式化輸入,printf是格式化輸出。
cin是輸入流,cout是輸出流。效率稍低,但書寫簡便。

根本原因百度有解釋,OJ上一般使用scanf和printf比較好些!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章