%黈力 %小猫
先用树剖搞出DFS序,再用树状数组差分维护
答案
。
对于第一种操作,子树中的每一个点都被加上a,直接区间加就好了。
对于第二种操作,每个点增加的值为(deep[y]-deep[x]+1)*a,对此可以分成两部分,一部分是-(deep[x]-1)*a,同第一种操作一样处理,另一部分是deep[y]*a,可再开一个树状数组,这个数组对答案的贡献需要乘上deep。
询问时将两个树状数组的贡献加起来就好了。
long long只开了一半,调了好几个小时QAQ。
#include<cstdio>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define LL long long
const int N=100005;
int he[N],ne[N<<1],to[N<<1],tot;
int n,m;LL w[N];
int dad[N],son[N],pre[N],sz[N],dep[N],dfn[N],cnt;
LL T1[N],T2[N];
void add(int x,int y){
to[++tot]=y;ne[tot]=he[x];he[x]=tot;
}
void DFS1(int x){
int y,i;sz[x]=1;
for(i=he[x];i;i=ne[i])if((y=to[i])!=pre[x]){
dep[y]=dep[pre[y]=x]+1;
w[y]+=w[x];
DFS1(y);
if(sz[y]>sz[son[x]])son[x]=y;
sz[x]+=sz[y];
}
}
void DFS2(int x){
int y,i;
dad[x]=son[pre[x]]==x?dad[pre[x]]:x;
dfn[x]=++cnt;
if(son[x])DFS2(son[x]);
for(i=he[x];i;i=ne[i])if(!dfn[y=to[i]])
DFS2(y);
}
void ins(LL T[],int x,LL y){
for(;x<=n+1;x+=x&-x)T[x]+=y;
}
LL query(LL T[],int x){
LL z=0;
for(;x;x-=x&-x)z+=T[x];
return z;
}
int main(){
int i,x,y;LL a;
scanf("%d%d",&n,&m);tot=1;
rep(i,1,n)scanf("%lld",&w[i]);
rep(i,2,n){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
DFS1(1);DFS2(1);
rep(i,1,n)ins(T1,dfn[i],w[i]),ins(T1,dfn[i]+1,-w[i]);
while(m--){
scanf("%d%d",&i,&x);y=dfn[x];
if(i==3)
printf("%lld\n",dep[x]*query(T2,y)+query(T1,y));
else{
scanf("%lld",&a);
if(i==1)ins(T1,y,a),ins(T1,y+sz[x],-a);
else ins(T1,y+sz[x],(dep[x]-1)*a),ins(T1,y,(1-dep[x])*a),ins(T2,y+sz[x],-a),ins(T2,y,a);
}
}
return 0;
}