Description
"奋战三星期,造台计算机"。小G响应号召,花了三小时造了台普通计算姬。普通计算姬比普通计算机要厉害一些
。普通计算机能计算数列区间和,而普通计算姬能计算树中子树和。更具体地,小G的计算姬可以解决这么个问题
:给定一棵n个节点的带权树,节点编号为1到n,以root为根,设sum[p]表示以点p为根的这棵子树中所有节点的权
值和。计算姬支持下列两种操作:
1 给定两个整数u,v,修改点u的权值为v。
2 给定两个整数l,r,计算sum[l]+sum[l+1]+....+sum[r-1]+sum[r]
尽管计算姬可以很快完成这个问题,可是小G并不知道它的答案是否正确,你能帮助他吗?
Input
第一行两个整数n,m,表示树的节点数与操作次数。
接下来一行n个整数,第i个整数di表示点i的初始权值。
接下来n行每行两个整数ai,bi,表示一条树上的边,若ai=0则说明bi是根。
接下来m行每行三个整数,第一个整数op表示操作类型。
若op=1则接下来两个整数u,v表示将点u的权值修改为v。
若op=2则接下来两个整数l,r表示询问。
N<=10^5,M<=10^5
0<=Di,V<2^31,1<=L<=R<=N,1<=U<=N
Output
对每个操作类型2输出一行一个整数表示答案。
Sample Input
6 4
0 0 3 4 0 1
0 1
1 2
2 3
2 4
3 5
5 6
2 1 2
1 1 1
2 3 6
2 3 5
0 0 3 4 0 1
0 1
1 2
2 3
2 4
3 5
5 6
2 1 2
1 1 1
2 3 6
2 3 5
Sample Output
16
10
9
10
9
HINT
Source
感受到树上分块的邪恶力量!!! %%%XLightGod;
貌似这题有很多种做法,主要是连续编号的子树和不是很好搞!!!
直接讲树上分块的做法好了,不想绕圈子:
子树和依据我们以前打树链剖分的时候(其实应该叫轻重链剖分,今天听到了一位NOI金牌爷说了个叫长链剖分的鬼玩意);
我们知道一个点的子树其实就是一段连续的dfs序;
首先对[1,n]分块,想到分块查询的基本思想
那么每次询问相当与是若干个整块加上剩下的几个点;
我们一步一步来解决:
首先对于每一个块的可以通过统计每个点在子树中出现的次数,那么我们可以通过O(n)的时间计算出整块贡献;
接下来的瓶颈就在于解决如何快速O(1)求出每个点的子树和
根据子树是dfs序中连续的一段我们可以考虑对dfs序进行分块,然后统计所有块的前缀和以及每个块自己内部的前缀和,通过前缀和的基本操作可以O(1)求解
附上代码:
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<queue> #include<set> #include<cstdlib> #include<cstring> #include<string> #include<ctime> #define lson num<<1 #define rson num<<1|1 using namespace std; typedef long long ll; const int N=100001; int gi() { int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } int head[N],to[N*2],nxt[N*2]; int v[N],pos[N],kp[N],dfn[N],id[N],block,num[320][N],fa[N],end[N]; int n,m,cnt,tt,cnt2,root; unsigned long long tot1[N],tot2[320],tot3[320]; void dfs(int x,int f){ dfn[x]=++tt;id[tt]=x; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(y!=f){ fa[y]=x;dfs(y,x); } } end[x]=tt; } inline void lnk(int x,int y){ to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt; to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt; } unsigned long long query(int x){return tot3[pos[x]-1]+tot1[x];} inline void make_tot1(){ for(int i=1;i<=n;i++) tot1[dfn[i]]=v[i]; for(int i=1;i<=n;i++) {if(kp[i]!=1) tot1[i]+=tot1[i-1];} } inline void make_tot2(){ for(int i=1;i<=cnt2;i++) for(int j=1;j<=n;j++) num[i][id[j]]=num[i][fa[id[j]]]+(pos[id[j]]==i); for(int i=1;i<=cnt2;i++) for(int j=1;j<=n;j++) tot2[i]+=(unsigned long long)1ll*num[i][j]*v[j]; } inline void make_tot3(){ for(int i=1;i<=n;i++) tot3[pos[dfn[i]]]+=v[i]; for(int i=1;i<=cnt2;i++) tot3[i]+=tot3[i-1]; } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); n=gi(),m=gi();int x,y; for(int i=1;i<=n;i++) v[i]=gi(); for(int i=1;i<=n;i++){ x=gi(),y=gi(); if(x!=0) lnk(x,y); else root=y; } int block=sqrt(n); if(n%block) cnt2=n/block+1; else cnt2=n/block; for(int i=1;i<=n;i++){ pos[i]=(i-1)/block+1; kp[i]=(i-1)%block+1; } dfs(root,0); make_tot1();make_tot2();make_tot3();int flag; while(m--){ flag=gi(); if(flag==1){ x=gi(),y=gi()-v[x]; for(int i=dfn[x];i<=n&&pos[i]==pos[dfn[x]];i++) tot1[i]+=y; for(int i=1;i<=cnt2;i++) tot2[i]+=(unsigned long long)1ll*num[i][x]*y; for(int i=pos[dfn[x]];i<=cnt2;i++) tot3[i]+=y; v[x]+=y; } else{ int l=gi(),r=gi();unsigned long long ans=0; if(pos[l]==pos[r]){ for(int i=l;i<=r;i++) ans+=query(end[i])-query(dfn[i]-1); } else{ for(int i=l;pos[i]==pos[l];i++) ans+=query(end[i])-query(dfn[i]-1); for(int i=r;pos[i]==pos[r];i--) ans+=query(end[i])-query(dfn[i]-1); for(int i=pos[l]+1;i<pos[r];i++) ans+=tot2[i]; } printf("%llu ",ans); } } return 0; }