这是一道单点修改,区间查询的线段树。
需要实现的操作有三个:建树,更新与查询。
首先,线段树用结构体维护,如下:
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
(注:第二道题是求区间最值,和模板稍有不同。)