• 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

    Sample Output

    16
    10
    9
     
     
    正解:分块
    解题报告:
      这道题折腾了我一个上午…
      我开始傻傻地写了一个带$log$的算法,在$BZOJ$老爷机上死活跑不过去…
      考虑把$1$到$n$分块,那么每次对于整块我直接调用答案,对于剩余的部分我暴力做,一个一个在$dfs$序上查询就可以了。
      那么我们如何来处理整块的答案呢,我开始的做法很辣鸡,就是对于块内每个点$dfs$序区间打上一个$+1$的标记,然后每个块都要对$1$到$n$求一遍$1$到$n$对这个块的贡献,这样的话就带了个$log$,跑得很慢。
      实际上我只需要在做每个块时,$dfs$一遍整棵树,顺便给每个点打个标记,就可以直接做完了不需要$log$…
      这样我就可以$nsqrt{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
    ",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
    ",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
    ",ans);
    		}
    	}
    	//cout<<endl<<clock()<<endl;
    }
    
    int main()
    {
        work();
        return 0;
    }
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    

      

  • 相关阅读:
    随便练习的进制转换
    回顾快速排序
    常用c++函数
    POJ 1163 The Triangle
    HDU 1155 Bungee Jumping
    ZOJ 3861 Valid Pattern Lock
    POJ 1273 Drainage Ditches
    Hrbust 2240 土豪的时代
    POJ 3468 A Simple Problem with Integers
    POJ 1061 青蛙的约会
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6514792.html
Copyright © 2020-2023  润新知