一。形象理解:
本题是“ 单点更新 ”的模板题。
线段树几个必要函数,1.建树BuildTree, 2.更新Update , 3. 查询Query
1. 建树:
BuildTree(int l, int r, int k) //建立一颗从l 到 r的二叉树,k表示当前建立的点。
{
T[k].l = l;
T[k].r = r;
if(l == r) {
scanf("%d",&T[k].v"); //“递归出口:”,到了叶子节点,左右相等,故输入数值即可。
return;
}
//“递归过程:”
int mid = (l + r) / 2;
BuildTree(l, mid, k*2); //递归建立左节点,序号为2 * k;
BuildTree(mid+1, r, k*2+1); //递归建立右节点。
T[k].v = T[2*k].v + T[2*k+1].v; //递归后更新这个节点的值。
}
2. 更新:
void Update(int c, int v, int k) { //给第c个物体增加v的价值,当前的位置为k 。(1 <= c <= N)
if(T[c].l == T[c].r && T[c].l == c) {
T[k].v += v; //“递归出口:”,如果找到了相应值,增加该节点值并返回。
return;
}
int mid = (T[c].l + T[c].r)/2; //“递归过程:”,二分直到找到c点。
if(c < mid) Update(c, v, 2*k);
else Update(c, v, 2*k+1);
T[k].v = T[2*k].v + T[2*k+1].v; //记得要更新值啊
}
3. 查询:
int ans; //ans保存的是查询的和,记得每次要归 0
void Query(int l, int r, int k) { //查询操作,从l点到r点的sum,从k点查询。
if( l > T[k].r || r < T[k].l) { //出界,out
return;
}
if( l <= T[k].l && r >= T[k].r) { //如果查询的内容在这个区间内,添加ans
ans += T[k].v; //“递归出口:”
return;
}
//“递归过程:”如果在左边显然是查询左边,如果在右边
int mid = (T[k].l + T[k].r) / 2; //显然是查询右边,如果横跨左右,则查询在左边查询左
if(r <= mid) //边的部分,在右边查询右边的方法。
Query(l, r, 2*k);
else if(l > mid)
Query(l, r, 2*k+1);
else {
Query(1, mid, 2*k);
Query(mid+1, r, 2*k+1);
}
}
二。实例代码:
#include <iostream>
#include <cstdio>
using namespace std;
int t, n, p = 0;
const int N = 100010;
struct segTree{
int l, r;
int v;
}T[N*4];
void Build(int l, int r, int k) {
T[k].l = l, T[k].r = r;
if(l == r) {
scanf("%d", &T[k].v);
return;
}
int mid = (l + r) / 2;
Build(l, mid, k*2);
Build(mid+1, r, k*2+1);
T[k].v = T[k*2].v + T[k*2+1].v;
}
int ans;
void Update(int c, int v, int k) {
if(T[k].l == T[k].r && T[k].l == c) {
T[k].v += v;
return;
}
int mid = (T[k].l + T[k].r)/2;
if(c <= mid) Update(c, v, k*2);
else Update(c, v, k*2+1);
T[k].v = T[k*2].v + T[k*2+1].v;
}
void Query(int l, int r, int k) {
//printf("Query(%d, %d, %d)\n",l, r, k); //注释掉这句话可以看到递归的详细过程
if(l > T[k].r || r < T[k].l) {
return;
}
if(l <= T[k].l && r >= T[k].r) {
ans += T[k].v;
return;
}
int mid = (T[k].l + T[k].r) / 2;
if(r <= mid) Query(l, r, k*2);
else if( l > mid) Query(l, r, k*2+1);
else {
Query(l, mid, k*2);
Query(mid+1, r, k*2+1);
}
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d", &n);
Build(1, n, 1);
char op[10];
printf("Case %d:\n", ++p);
while(scanf("%s", op), op[0]!='E') {
int u, v;
scanf("%d%d", &u, &v);
if(op[0] == 'A') {
Update(u, v, 1);
}else if (op[0] == 'S') {
Update(u, -v, 1);
}else {
ans = 0;
Query(u, v, 1);
printf("%d\n", ans);
}
}
}
return 0;
}
三。相似例题:
HDU1754 I hate it
四。如何训练:
搜索网传的Kuangbin带你飞【线段树系列】https://cn.vjudge.net/article/187,练习其中的代码。