1. 程式人生 > >題解 P2068 【統計和】

題解 P2068 【統計和】

這是一道單點修改,區間查詢的線段樹。

需要實現的操作有三個:建樹,更新與查詢。

首先,線段樹用結構體維護,如下:

1 struct node {
2     int l, r;
3     int val;
4 } tree[maxn * 4];

 

其中l, r表示節點所表示區間左右端點,而val則是區間和。

Build函式如下:

1 void Build(int l, int r, int pos) {
2     tree[pos].l = l;
3     tree[pos].r = r;
4     tree[pos].val = 0;
5     if
(l == r) return ; 6 int mid = (l + r) / 2; 7 Build(l, mid, pos * 2); 8 Build(mid + 1, r, pos * 2 + 1); 9 }

 

這個函式就是初始化整棵線段樹,沒有什麼特別需要解釋的。

Update函式如下:

 1 void Update(int x, int val, int pos) {
 2     if(tree[pos].r == tree[pos].l) {
 3         tree[pos].val += val;
 4         return
; 5 } 6 7 int mid = (tree[pos].l + tree[pos].r) / 2; 8 if(x <= mid) Update(x, val, pos * 2); 9 else Update(x, val, pos * 2 + 1); 10 tree[pos].val = tree[pos * 2].val + tree[pos * 2 + 1].val; 11 }

 

x表示需要修改的節點,val表示需要增加的值,pos表示當前節點。

如果走到了葉節點上,直接修改,然後結束。

否則,判斷x在當前節點的哪一個兒子上,向下。

最後更新當前節點的值。

Query函式如下:

 1 int Query(int l, int r, int pos) {
 2     if(tree[pos].l >= l && tree[pos].r <= r) {
 3         return tree[pos].val;
 4     }
 5     int mid = (tree[pos].l + tree[pos].r) / 2;
 6     int ans = 0;
 7     if(l <= mid) ans += Query(l, r, pos * 2);
 8     if(mid < r) ans += Query(l, r, pos * 2 + 1);
 9     return ans;
10 }

 

如果當前節點的整個區間都已經被包含在所求的區間內了,那麼不用再進行劃分,返回區間值即可。

否則分三種情況討論:

1. 若所求區間只與左兒子有交集,移到左兒子。

2. 若只與右兒子有交集,移到右兒子。

3. 若被當前節點覆蓋,左兒子右兒子都算。


以上就是思路及關鍵程式碼。

順便推薦兩道經典的單點修改題目(反正我是做了的):

1. HDU1166 敵兵佈陣

2. HDU1754 I hate it

(注:第二道題是求區間最值,和模板稍有不同。)