题解 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

(注:第二道题是求区间最值,和模板稍有不同。)

原文地址:https://www.cnblogs.com/ilverene/p/9818989.html