bzoj 4765: 普通计算姬
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
思路
分块分块再分块,
假设块的长度是m,
可以先对编号分块,预处理出每个点对每一块的贡献(即这一块中有多少个x的祖先)。
这样时间复杂度n^2/m,空间复杂度n^2/m,为了不mle,m适宜<500。
这样修改操作就可以O(n/m)完成。
考虑询问操作,中间的完整块很好处理O(n/m),但左右端的残余块难处理。
可以扫残余块中的每个点,计算它们子树和的和,大约要计算m次子树和,即查询m次。
考虑怎样很快的计算子树和。
一种是log的做法,dfs序建树状数组,修改O(logn),查询O(logn)。但是这样查询多了一个log,不优。
考虑,每次操作,修改只进行了1次,查询进行了m次。所以考虑使修改变慢,查询变快。
所以,再对dfs序进行分块即可。这样修改O(n/m),查询1次O(1),非常优越。
时间复杂度n^2/m+nm=n*(n/m+m)。所以当m=sqrt(n)的时候,时间复杂度为O(n sqrt(n))
由于这种做法常数较大,虽然是O(n sqrt(n))的,有可能比那些带log的程序要慢(这也很尴尬啊)
代码
1 #include <cmath> 2 #include <cstdio> 3 #include <iostream> 4 using namespace std; 5 int read() 6 { 7 char c=getchar();int d=0,f=1; 8 for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1; 9 for(;c>='0'&&c<='9';d=d*10+c-48,c=getchar()); 10 return d*f; 11 } 12 typedef unsigned long long ull; 13 typedef long long ll; 14 const int N=100002; 15 int M,n,m,cnt,tot; 16 int tmp[N],fa[N],bl[N],L[N],R[N],w[N*2],head[N],size[N],pos[N],t[320][N]; 17 ll s[N*2],bs[N*2],a[N*2]; 18 struct xint{int to,next;}e[N*2]; 19 inline void addedge(int x,int y) 20 { 21 e[++cnt]=(xint){y,head[x]}; 22 head[x]=cnt; 23 } 24 inline void add(int x,int s) 25 { 26 for(int i=R[x];i<=R[n];i+=M)a[i]+=s; 27 for(int i=x;i<=R[x]-1;++i)a[i]+=s; 28 } 29 inline ll ask(int x) 30 { 31 int A=pos[x]+size[x]-1,B=pos[x]-1; 32 return ((A%M)?a[L[A]-1]+a[A]:a[A])-((B%M)?a[L[B]-1]+a[B]:a[B]); 33 } 34 inline void dfs(int x) 35 { 36 tmp[bl[x]]++; 37 for(int i=1;i<=bl[n];++i)t[i][x]=tmp[i]; 38 pos[x]=++tot; 39 size[x]=1; s[x]=w[x]; 40 for(int i=head[x];i;i=e[i].next) 41 { 42 int y=e[i].to; 43 if(y==fa[x])continue; 44 fa[y]=x;dfs(y); 45 size[x]+=size[y]; 46 s[x]+=s[y]; 47 } 48 tmp[bl[x]]--; 49 } 50 inline void init() 51 { 52 n=read()+1,m=read(); 53 M=int(sqrt(n)); 54 for(int i=1;i<=n;++i)bl[i]=(i-1)/M+1; 55 for(int i=1;i<=n;++i)L[i]=bl[i]*M-M+1,R[i]=bl[i]*M; 56 for(int i=2;i<=n;++i)w[i]=read(); 57 for(int i=2;i<=n;++i) 58 { 59 int x=read()+1,y=read()+1; 60 addedge(x,y); 61 addedge(y,x); 62 } 63 dfs(1); 64 for(int i=1;i<=n;++i)bs[bl[i]]+=s[i]; 65 for(int i=1;i<=n;++i)add(pos[i],w[i]); 66 } 67 inline ull solve(int x,int y) 68 { 69 ull res=0; 70 if(bl[x]==bl[y]) 71 { 72 for(int i=x;i<=y;++i)res+=ask(i); 73 return res; 74 } 75 for(int i=x;i<=R[x];++i)res+=ask(i); 76 for(int i=L[y];i<=y;++i)res+=ask(i); 77 for(int i=bl[x]+1;i<=bl[y]-1;++i)res+=bs[i]; 78 return res; 79 } 80 inline void work() 81 { 82 while(m--) 83 { 84 int o=read(),x=read(),y=read(); 85 if(o==1) 86 { 87 x++; 88 ll dw=y-w[x]; 89 for(int i=1;i<=bl[n];++i) 90 bs[i]+=dw*t[i][x]; 91 add(pos[x],dw); 92 w[x]=y; 93 }else 94 if(o==2) 95 { 96 x++,y++; 97 ull ans=solve(x,y); 98 printf("%llu\n",ans); 99 } 100 } 101 } 102 int main() 103 { 104 init(); 105 work(); 106 }