解题思路
树上的分块题,,对于修改操作,每次修改只会对他父亲到根这条链上的元素有影响;对于查询操作,每次查询[l,r]内所有元素的子树,所以就考虑dfn序,进标记一次,出标记一次,然后子树就是进与出之间的所有元素。分块后预处理出每个点修改对当前块多少个元素的影响f[i][j],再预处理出每个块的和,然后修改时利用f数组暴力扫一遍所有块,查询是大块直接查sum,小块用树状数组查。要开unsigned long long
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath> using namespace std;
const int MAXN = ;
const int SIZE = ;
typedef unsigned long long ULL; inline int rd(){
int x=,f=;char ch=getchar();
while(!isdigit(ch)) {f=ch=='-'?:;ch=getchar();}
while(isdigit(ch)) {x=(x<<)+(x<<)+ch-'';ch=getchar();}
return f?x:-x;
} int n,m,val[MAXN],f[MAXN][SIZE],bl[MAXN],l[SIZE],r[SIZE],siz,num,rt;
int head[MAXN],cnt,to[MAXN<<],nxt[MAXN<<],in[MAXN],out[MAXN],pos[MAXN];
int dfn;
ULL sum[MAXN],Sum[SIZE],ans,t[MAXN]; inline void add(int bg,int ed){
to[++cnt]=ed,nxt[cnt]=head[bg],head[bg]=cnt;
} void update(int x,int y){
for(;x<=n;x+=x&-x) t[x]+=y;
} ULL query(int x){
ULL ret=;
for(;x;x-=x&-x) ret+=t[x];
return ret;
} ULL Query(int x,int y){
ULL ret=;
if(bl[x]==bl[y]) {
for(register int i=x;i<=y;i++) ret+=query(out[i])-query(in[i]-);
return ret;
}
for(register int i=x;i<=r[bl[x]];i++) ret+=query(out[i])-query(in[i]-);
for(register int i=l[bl[y]];i<=y;i++) ret+=query(out[i])-query(in[i]-);
for(register int i=bl[x]+;i<bl[y];i++) ret+=Sum[i];
return ret;
} void dfs(int x,int fa){
in[x]=++dfn;pos[bl[x]]++;sum[x]=val[x];update(in[x],val[x]);
for(register int i=;i<=num;i++) f[x][i]=pos[i];
for(register int i=head[x];i;i=nxt[i]){
int u=to[i];if(u==fa) continue;
dfs(u,x);sum[x]+=sum[u];
}
out[x]=dfn;pos[bl[x]]--;Sum[bl[x]]+=sum[x];
} int main(){
n=rd(),m=rd();int op,x,y;
siz=sqrt(n)+;num=n/siz;if(n%siz) num++;
for(int i=;i<=n;i++) val[i]=rd(),bl[i]=(i-)/siz+;
for(int i=;i<=num;i++) l[i]=(i-)*siz+,r[i]=i*siz;
r[num]=n;
for(int i=;i<=n;i++){
x=rd(),y=rd();
if(x== || y==) rt=(x|y);
else add(x,y),add(y,x);
}
dfs(rt,);
while(m--){
op=rd(),x=rd(),y=rd();
if(op==){
int now=y-val[x];
for(register int i=;i<=num;i++)
Sum[i]+=(ULL)f[x][i]*now;
update(in[x],y-val[x]);val[x]=y;
}
else printf("%llu\n",Query(x,y));
}
return ;
}