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$…
  这样我就可以$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;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

posted @ 2017-03-07 14:58  ljh_2000  阅读(1173)  评论(0编辑  收藏  举报