题意:
给一颗n个节点的树,每个节点有点权,编号为1的节点是树的根
有m次操作:
操作1是给x加上val,然后给x的所有子节点加上-val,x的子节点的子节点加上-(-val),不断传递
操作2是查询点x的点权
解法:
显然题目是子树问题,先dfs序处理成序列,然后考虑用线段树做
难点主要在于如何处理每次向下传递的时候,添加的数值要取相反数。
设d(x)为x到根的距离的奇偶性(0或者1)
修改x的时候,以x为根的子树中,所有与x深度奇偶性相同的点,都是+val
而所有与x深度奇偶性不同的点,都是-val
那么可以将奇数层和偶数层的点分开,按奇偶性建立两颗线段树
设x的子树范围为[L,R],
在修改x的时候,与x奇偶性相同的树[L,R]添加val,这样做是为了将[L,R]中与x奇偶性相同的点全部加上val
并且与x奇偶性不同的树[L,R]减少val,这样做是为了将[L,R]中与x奇偶性不同的点全部加上val
奇数树中,深度为偶数的位置是没用的,直接忽略,偶数树同理
总结:这题按奇偶性建树避免了对每层+val和-val的讨论
似乎可以看作是一种处理分层子树问题的套路
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
vector<int>g[maxm];
int L[maxm],R[maxm];
int rk[maxm],idx;
int d[maxm];
int v[maxm];
int n,m;
struct Node{
int a[maxm<<2];
void pushdown(int node){
if(!a[node])return ;
a[node*2]+=a[node];
a[node*2+1]+=a[node];
a[node]=0;
}
void update(int st,int ed,int val,int l,int r,int node){
if(st<=l&&ed>=r){
a[node]+=val;
return ;
}
pushdown(node);
int mid=(l+r)/2;
if(st<=mid)update(st,ed,val,l,mid,node*2);
if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
}
int ask(int x,int l,int r,int node){
if(l==r)return a[node];
pushdown(node);
int mid=(l+r)/2;
if(x<=mid)return ask(x,l,mid,node*2);
else return ask(x,mid+1,r,node*2+1);
}
}T[2];
void dfs(int x,int fa,int dep){
d[x]=dep;
L[x]=++idx;
rk[idx]=x;
for(int v:g[x]){
if(v==fa)continue;
dfs(v,x,dep^1);
}
R[x]=idx;
}
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&v[i]);
for(int i=1;i<n;i++){
int a,b;scanf("%d%d",&a,&b);
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1,-1,0);
while(m--){
int op;scanf("%d",&op);
if(op==1){//updata
int x,val;scanf("%d%d",&x,&val);
T[d[x]].update(L[x],R[x],val,1,n,1);//x子树中和x一样深度的都要加
T[d[x]^1].update(L[x],R[x],-val,1,n,1);//x子树中和x不一样深度的都要减
}else{//ask
int x;scanf("%d",&x);
int ans=v[x]+T[d[x]].ask(L[x],1,n,1);//因为v[x]开始没有加到线段树里面,所以要加上
printf("%d\n",ans);
}
}
return 0;
}