题目:

给一棵以为根的有根树,点权
个操作,操作有两种类型:
:表示将节点的权值加上
:表示求节点的子树上所有节点的和(包括结点本身);


做法:

单点修改,求子树上的点权和。我们通过序可以将一整棵子树上映射到序列中连续的一段上。所以问题就变成了,单点修改,区间和查询。线段树或树状数组就行了。


代码:

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define debug(a) cout << #a ": " << a << endl
using namespace std;
typedef long long ll;
const int N = 1e6 + 7;
struct edge{
    int v, nxt;
}e[N<<1];
int tot, head[N];
int n, m, k, a[N];
int dfn[N], cnt, in[N], out[N];
ll T[N];
void init(void){
    tot = 0;
    memset(head, -1, sizeof head);
}
void addedge(int u, int v){
    e[tot] = (edge){v, head[u]};
    head[u] = tot++;
}
void dfs(int u, int p){
    dfn[++cnt] = u, in[u] = cnt;
    for (int i = head[u]; i != -1; i = e[i].nxt){
        int v = e[i].v;
        if (v == p) continue;
        dfs(v, u);
    }
    out[u] = cnt;
}
int lowbit(int x){
    return x&(-x);
}
void add(int x, int y, int n){
    //printf("add x %d y %d\n", x, y);
    for (int i = x; i <= n; i += lowbit(i)) T[i] += y;
}
ll ask(int x){
    //printf("ask x %d\n", x);
    ll ans = 0;
    for (int i = x; i >= 1; i -= lowbit(i)) ans += T[i];
    return ans;
}
int main(void){
    init();
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    for (int i = 0; i < n-1; ++i){
        int u, v; scanf("%d%d", &u, &v);
        addedge(u, v), addedge(v, u);
    } 
    dfs(k, k);
    for (int i = 1; i <= n; ++i) add(in[i], a[i], cnt);
    for (int i = 1; i <= m; ++i){
        int op, x, y;
        scanf("%d", &op);
        if (op == 1){
            scanf("%d%d", &x, &y);
            add(in[x], y, cnt);
        }else{
            scanf("%d", &x);
            printf("%lld\n", ask(out[x])-ask(in[x]-1));
        }
    }   
    return 0;
}