BZOJ4765 普通计算姬
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
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
正解:分块
解题报告:
解题报告:
这道题折腾了我一个上午…
我开始傻傻地写了一个带$log$的算法,在$BZOJ$老爷机上死活跑不过去…
考虑把$1$到$n$分块,那么每次对于整块我直接调用答案,对于剩余的部分我暴力做,一个一个在$dfs$序上查询就可以了。
那么我们如何来处理整块的答案呢,我开始的做法很辣鸡,就是对于块内每个点$dfs$序区间打上一个$+1$的标记,然后每个块都要对$1$到$n$求一遍$1$到$n$对这个块的贡献,这样的话就带了个$log$,跑得很慢。
实际上我只需要在做每个块时,$dfs$一遍整棵树,顺便给每个点打个标记,就可以直接做完了不需要$log$…
这样我就可以$n\sqrt{n}$地预处理出每个点对每个块的答案。
那么修改操作只需要根据我要修改的点对所有块的影响,修改一下每个块的$sum$即可。
那么问题只剩下了如何处理快速查询$dfs$序上区间和了。
我开始无脑上树状数组,发现尽管树状数组常数小,但是终究是多了一个$log$...
仔细思考发现,对于这个操作只需要支持单点修改和区间查询,树状数组是$O(logn)-O(logn)$的,而我的修改因为第一步对整块的修改已经是根号级别了,那此处的$log$未免显得浪费了,而查询的时候又会多一个$log$,所以我们考虑能否把查询变为$O(1)$。
不难发现我只需要对于$dfs$序区间另外分块,就可以做到$O(sqrt(n))-O(1)$的了,然后这道题就做完了...
这道题启示窝,要不断思考如何平衡不同操作之间的复杂度和常数优化...
还有$unsigned$ $long$ $long$!!!
//It is made by ljh2000 //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。 #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <ctime> #define lc root<<1 #define rc root<<1|1 using namespace std; typedef long long LL; typedef unsigned long long ULL; const int MAXN = 100011; const int MAXM = 200011; const int MAXS = 2017; int n,m,rt,ecnt,first[MAXN],to[MAXM],next[MAXM],wei[MAXN]; int dfn[MAXN],pre[MAXN],last[MAXN],belong[MAXN],block,kcnt,L[MAXS],R[MAXS]; bool in[MAXN]; LL S[MAXN][320],val[MAXN],a[MAXN],qian[MAXN]; ULL sum[MAXS],ans,A; namespace FK{ LL c[MAXS],ss[MAXN]; inline void add(int x,LL val){ int bel=belong[x]; for(int i=x;i<=R[bel];i++) ss[i]+=val; for(int i=bel;i<=kcnt;i++) c[i]+=val; } inline LL Q(int l,int r){//查询dfs序上的区间[l,r] LL tot=0; int ll,rr; ll=belong[l]; rr=belong[r]; if(ll==rr) { if(l==L[ll]) tot=ss[r]; else tot=ss[r]-ss[l-1]; return tot; } if(l!=L[ll]) { tot+=ss[L[ll+1]-1]-ss[l-1]; ll++; } if(r!=R[rr]) { tot+=ss[r]; rr--; } if(ll<=rr) tot+=c[rr]-c[ll-1]; return tot; } } inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; } inline int getint(){ int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } inline void dfs(int x,int fa){ dfn[x]=++ecnt; pre[ecnt]=x; for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; dfs(v,x); } last[x]=ecnt; } inline void dfs2(int x,int fa,int lei,int K){ if(in[x]) lei++; S[x][K]=lei; for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; dfs2(v,x,lei,K); } } inline void build(){ ecnt=0; dfs(rt,0); for(int i=1;i<=n;i++) qian[i]=qian[i-1]+a[pre[i]]; //block=3; block=330; for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1; kcnt=n/block; if(n%block) kcnt++; for(int i=1;i<=kcnt+1;i++) L[i]=n+1; for(int i=1;i<=n;i++) L[belong[i]]=min(L[belong[i]],i),R[belong[i]]=i; for(int i=1;i<=kcnt;i++) { FK::ss[ L[i] ]=a[pre[ L[i] ]]; FK::c[i]=FK::c[i-1]+a[pre[ L[i] ]]; for(int j=L[i]+1;j<=R[i];j++) { FK::c[i]+=a[ pre[j] ]; FK::ss[j]=FK::ss[j-1]+a[ pre[j] ]; } } for(int i=1;i<=kcnt;++i) { for(int j=L[i];j<=R[i];++j) { in[j]=1; sum[i]+=qian[last[j]]-qian[dfn[j]-1]; } dfs2(rt,0,0,i); for(int j=L[i];j<=R[i];++j) in[j]=0; } //cout<<clock()<<endl; } inline void work(){ //printf("%d\n",sizeof(S)/1024/1024); n=getint(); m=getint(); int type,x,y; for(int i=1;i<=n;i++) a[i]=getint(); for(int i=1;i<=n;i++) { x=getint(); y=getint(); if(x>y) swap(x,y); if(x==0) { rt=y; continue; } link(x,y); link(y,x); } build(); int ll,rr; LL cha; while(m--) { type=getint(); x=getint(); y=getint(); if(type==1) { cha=y-a[x]; FK::add(dfn[x],y-a[x]);//单点修改... //还需要修改对每个块的贡献... for(int i=1;i<=kcnt;++i) sum[i]+=cha*S[x][i]; a[x]=y; } else { ans=0; if(belong[x]==belong[y] || belong[x]==belong[y]-1){ for(int i=x;i<=y;++i) ans+=FK::Q(dfn[i],last[i]); printf("%llu\n",ans); continue; } if(x==L[ belong[x] ]) ll=belong[x]; else ll=belong[x]+1; if(y==R[ belong[y] ]) rr=belong[y]; else rr=belong[y]-1; for(int i=ll;i<=rr;++i) ans+=sum[i]; for(int i=x;i<L[ll];++i) ans+=FK::Q(dfn[i],last[i]); for(int i=R[rr]+1;i<=y;++i) ans+=FK::Q(dfn[i],last[i]); printf("%llu\n",ans); } } //cout<<endl<<clock()<<endl; } int main() { work(); return 0; } //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!