学长的浅谈线段树~~~

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int T, n, l, r, cas = 0;
int f[N * 4]; // f[i]表示i表示的某段区间的和,这种写法的线段树通常需要实际节点数四倍的空间
char s[5];

/*
        1
    2        3
  4      5      6      7

线段树上对应的节点标号
对应于4个人的话,每个节点的区间如下:
          [1,4]
    [1,2]        [3,4]
[1,1]  [2,2] [3,3]  [4,4]
*/

void build(int x, int l, int r)//建树
{
    //x 节点编号 [l,r]节点表示的区间
    if (l == r) scanf("%d", &f[x]);
    else
    {
        int mid = (l + r) / 2;
        //把[l,r]分为[l,mid]和[mid+1,r]两个区间
        build(x * 2, l, mid);
        build(x * 2 + 1, mid + 1, r);
        f[x] = f[x * 2] + f[x * 2 + 1];
        //区间和就是两个子区间的和相加
    }
}

int query(int x, int l, int r, int ll, int rr)//询问区间和
{
    //如果当前节点x代表的区间[l,r]∈[ll,rr]那么直接返回区间和即可。
    //不然递归下去判断左右子区间中是否有[ll,rr]的一部分,并求和。
    if (ll <= l&&r <= rr) return f[x];
    int mid = (l + r) / 2, res = 0;
    if (ll <= mid) res += query(x * 2, l, mid, ll, rr);
    if (rr > mid) res += query(x * 2 + 1, mid + 1, r, ll, rr);
    return res;
}

void add(int x, int l, int r, int u, int v)//更新结点的值
{
    f[x] += v; //让某个节点+v相当于所有包含该节点的区间的和都+v
    if (l == r) return;
    int mid = (l + r) / 2;
    //判断在该区间的左子区间还是右子区间
    if (u <= mid) add(x * 2, l, mid, u, v);
    else add(x * 2 + 1, mid + 1, r, u, v);
}

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        build(1, 1, n);  //初始化线段树上各节点的值
        printf("Case %d:\n", ++cas);
        while (scanf("%s", s), s[0] != 'E')
        {
            scanf("%d%d", &l, &r);
            if (s[0] == 'Q') printf("%d\n", query(1, 1, n, l, r));
            if (s[0] == 'A') add(1, 1, n, l, r);
            if (s[0] == 'S') add(1, 1, n, l, -r);
        }
    }
    return 0;
}