#线段树 #树状数组 #dfn

题意

  • n个结点,n-1条边,根为1
  • 给定每个点的权值v_i
  • 有m个操作,两种
    • 1 a x 将结点a的权值+x,结点a的儿子权值-x,结点a的孙子权值+x,以此类推
    • 2 a 求a节点的权值

思路

  • 对于每一个点的修改,在这个点的子树中,所有和它奇偶性相同的点操作是+,所有和它奇偶性不同的点操作是相反的
  • 可以维护两个线段树,一个维护深度为奇数的点,一个维护深度为偶数的点
  • 对于每次修改在两颗树上同步维护就行
  • 对于每次查询根据查询点的奇偶性单独输出即可
  • 线段树只维护变化量,最后输出加上初始值就行,避免建树初始化
  • 另一个思路是维护差分树状数组,代码量较小
  • 附一个差分树状数组的blog

代码

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

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

int tim;
int L[202020],R[202020],dfn[202020];
int dep[202020];
long long t[2][1010101];

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

void pushdown(int p,int flg){
    t[flg][p*2]+=t[flg][p];
    t[flg][p*2+1]+=t[flg][p];
    t[flg][p]=0;
}

void add(int p,int l,int r,int x,int y,int num,int flg){
    if(x<=l&&r<=y){
        t[flg][p]+=num;
        return ;
    }
    if(t[flg][p]!=0) pushdown(p,flg);
    int mid=(l+r)>>1;
    if(x<=mid) add(p*2,l,mid,x,y,num,flg);
    if(y>mid) add(p*2+1,mid+1,r,x,y,num,flg);
}

long long cal(int p,int l,int r,int x,int flg){
    if(l==r){
        return t[flg][p];
    }
    if(t[flg][p]!=0) pushdown(p,flg);
    int mid=(l+r)>>1;
    if(x>mid) return cal(p*2+1,mid+1,r,x,flg);
    else return cal(p*2,l,mid,x,flg);
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n,m;
    cin >> n >> m;
    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(1,0);
    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],R[a],x,dep[a]&1);
            add(1,1,n,L[a],R[a],-x,!(dep[a]&1));
        }else{
            int a;
            cin >> a;
            cout << cal(1,1,n,L[a],dep[a]&1)+v[a] << endl;
        }
    }
    return 0;
}