#线段树 #树状数组 #dfn序

题意

  • n个结点,n-1条边
  • 给定根k和每个点的权值v_i
  • 有m个操作,两种
    • 1 a x 将结点a的权值+x
    • 2 a 求a节点所在的子树的所有结点的权值和

思路

  • 用dfn把树转化成链
  • 线段树or树状数组维护单点修改区间查询

代码

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;

int v[1010101];
vector<int> G[1010101];

int tim;
int L[4040404],R[4040404],dfn[4040404];
long long t[4040404];

void dfs(int x,int fa){
    L[x]=++tim;
    dfn[tim]=x;
    for(auto i:G[x]){
        if(i==fa)continue;
        dfs(i,x);
    }
    R[x]=tim;
}


void build(int p,int l,int r){
    if(l==r){
        t[p]=v[dfn[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    t[p]=t[p*2+1]+t[p*2];    
}

void add(int p,int l,int r,int pos,int num){
    if(l==r){
        t[p]+=num;
        return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) add(p*2,l,mid,pos,num);
    else add(p*2+1,mid+1,r,pos,num);
    t[p]=t[p*2+1]+t[p*2];
}

long long cal(int p,int l,int r,int x,int y){
    if(x<=l&&r<=y){
        return t[p];
    }
    int mid=(l+r)>>1;
    if(x>mid) return cal(p*2+1,mid+1,r,x,y);
    if(y<=mid) return cal(p*2,l,mid,x,y);
    return cal(p*2+1,mid+1,r,x,y)+cal(p*2,l,mid,x,y);
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n,m,k;
    cin >> n >> m >> k;
    for(int i=1;i<=n;i++) cin >> v[i] ;
    for(int i=0;i<n-1;i++){
        int u,v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(k,0);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int op;
        cin >> op;
        if(op==1){
            int a,x;
            cin >> a >> x;
            add(1,1,n,L[a],x);
        }else{
            int a;
            cin >> a;
            cout << cal(1,1,n,L[a],R[a]) << endl;
        }
    }
    return 0;
}