題解 P2068 【統計和】
阿新 • • 發佈:2018-11-10
這是一道單點修改,區間查詢的線段樹。
需要實現的操作有三個:建樹,更新與查詢。
首先,線段樹用結構體維護,如下:
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
(注:第二道題是求區間最值,和模板稍有不同。)