码迷,mamicode.com
首页 > 其他好文 > 详细

[BZOJ3786]星系探索

时间:2017-09-16 20:28:28      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:一点   为什么   减法   define   前缀和   for   chain   clu   size   

挺好的题

看到移动子树和子树加法,首先应该想到用平衡树维护dfs序

到根路径之和?(陷入沉思

我们做一点微小的工作改动,把dfs序换成入栈出栈序列$s$,每个点入栈时记权值为正,出栈时记权值为负

容易看出$\sum\limits_{k=1}^{ind_i} s_k$就是根到$i$路径上的权值和,其中$ind_i$代表$i$入栈时在$s$中的位置

为什么?因为不在路径上的点入栈一次出栈一次,就抵消掉了

那么我们可以用splay来维护这个序列

移动子树:直接提取区间$[ind_i,oud_i]$然后接到新的位置,其中$oud_i$代表$i$出栈时在$s$中的位置

子树加法:同样提取区间$[ind_i,oud_i]$打标记

前缀和:提取区间$[1,ind_i]$直接输出

要维护的标记还挺多:这个点的权值,它是出栈还是入栈,以它为根的子树权值和,以它为根的子树大小,子树增加值

注意我们在记录子树大小时,对入栈点是加法,对出栈点是减法,所以‘子树大小‘实际上记录的是(入栈点大小为$1$,出栈点大小为$-1$)的加权子树大小

注意,这里因为有区间移动,所以提取区间$[l,r]$不是简单的splay($l-1$)然后splay($r+1$),而应该是splay($pre_l$)然后splay($next_r$),这里的$pre_l$是$l$的前驱,$next_r$是$r$的后继

移动$x$的子树到$y$时记得更新$x$和$y$的信息

I?人類~

 

#include<stdio.h>
#define ll long long
struct edge{
	int to,nex;
}e[100010];
int ch[200010][2],fa[200010],ind[100010],oud[100010],h[100010],nv[100010],root,tot,n;
ll sum[200010],laz[200010],v[200010],siz[200010],io[200010];
void add(int a,int b){
	tot++;
	e[tot].to=b;
	e[tot].nex=h[a];
	h[a]=tot;
}
void dfs(int x){
	tot++;
	sum[tot]=nv[x];
	v[tot]=nv[x];
	io[tot]=1;
	siz[tot]=1;
	ind[x]=tot;
	for(int i=h[x];i;i=e[i].nex)dfs(e[i].to);
	tot++;
	sum[tot]=-nv[x];
	v[tot]=-nv[x];
	io[tot]=-1;
	siz[tot]=-1;
	oud[x]=tot;
}
int build(int l,int r){
	int mid=(l+r)>>1,ls=0,rs=0;
	if(l<mid)ls=build(l,mid-1);
	if(mid<r)rs=build(mid+1,r);
	if(ls){
		ch[mid][0]=ls;
		fa[ls]=mid;
		sum[mid]+=sum[ls];
		siz[mid]+=siz[ls];
	}
	if(rs){
		ch[mid][1]=rs;
		fa[rs]=mid;
		sum[mid]+=sum[rs];
		siz[mid]+=siz[rs];
	}
	return mid;
}
void pushup(int x){
	sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+v[x];
	siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+io[x];
}
void pushdown(int x){
	if(laz[x]){
		if(ch[x][0]){
			sum[ch[x][0]]+=siz[ch[x][0]]*laz[x];
			v[ch[x][0]]+=io[ch[x][0]]*laz[x];
			laz[ch[x][0]]+=laz[x];
		}
		if(ch[x][1]){
			sum[ch[x][1]]+=siz[ch[x][1]]*laz[x];
			v[ch[x][1]]+=io[ch[x][1]]*laz[x];
			laz[ch[x][1]]+=laz[x];
		}
		laz[x]=0;
	}
}
void rot(int x){
	int y,z,B;
	y=fa[x];
	z=fa[y];
	if(root==y)root=x;
	int f=(ch[y][0]==x);
	B=ch[x][f];
	fa[x]=z;
	fa[y]=x;
	if(B)fa[B]=y;
	ch[x][f]=y;
	ch[y][f^1]=B;
	if(ch[z][0]==y)ch[z][0]=x;
	if(ch[z][1]==y)ch[z][1]=x;
	pushup(y);
	pushup(x);
}
void chain(int x){
	if(x!=root)chain(fa[x]);
	pushdown(x);
}
void splay(int x){
	int y,z;
	chain(x);
	while(x!=root){
		y=fa[x];
		z=fa[y];
		if(y==root)
			rot(x);
		else{
			if((ch[z][0]==y&&ch[y][0]==x)||(ch[z][1]==y&&ch[y][1]==x)){
				rot(y);
				rot(x);
			}else{
				rot(x);
				rot(x);
			}
		}
	}
}
int nexsc(int x){
	if(ch[x][1]){
		for(x=ch[x][1];ch[x][0];x=ch[x][0]);
		return x;
	}
	while(ch[fa[x]][0]!=x)x=fa[x];
	return fa[x];
}
int presc(int x){
	if(ch[x][0]){
		for(x=ch[x][0];ch[x][1];x=ch[x][1]);
		return x;
	}
	while(ch[fa[x]][1]!=x)x=fa[x];
	return fa[x];
}
ll query(int x){
	splay(nexsc(ind[x]));
	return sum[ch[root][0]];
}
void change(int x,int y){
	splay(presc(ind[x]));
	root=ch[root][1];
	splay(nexsc(oud[x]));
	int p=ch[root][0],i;
	ch[root][0]=0;
	siz[root]-=siz[p];
	sum[root]-=sum[p];
	root=fa[root];
	siz[root]-=siz[p];
	sum[root]-=sum[p];
	splay(ind[y]);
	root=ch[root][1];
	for(i=root;ch[i][0];i=ch[i][0]);
	splay(i);
	ch[root][0]=p;
	fa[p]=root;
	pushup(root);
	root=fa[root];
	pushup(root);
}
void modify(int x,ll y){
	if(x==1){
		laz[root]+=y;
		sum[root]+=siz[root]*y;
		v[root]+=io[root]*y;
		return;
	}
	splay(presc(ind[x]));
	root=ch[root][1];
	splay(nexsc(oud[x]));
	laz[ch[root][0]]+=y;
	sum[ch[root][0]]+=siz[ch[root][0]]*y;
	v[ch[root][0]]+=io[ch[root][0]]*y;
	pushup(root);
	root=fa[root];
	pushup(root);
}
int main(){
	int m,i,a,b;
	char s[5];
	scanf("%d",&n);
	for(i=2;i<=n;i++){
		scanf("%d",&a);
		add(a,i);
	}
	for(i=1;i<=n;i++)scanf("%d",nv+i);
	tot=0;
	dfs(1);
	root=build(1,n<<1);
	scanf("%d",&m);
	while(m--){
		scanf("%s",s);
		if(s[0]==‘Q‘){
			scanf("%d",&a);
			printf("%lld\n",query(a));
		}
		if(s[0]==‘C‘){
			scanf("%d%d",&a,&b);
			change(a,b);
		}
		if(s[0]==‘F‘){
			scanf("%d%d",&a,&b);
			modify(a,(ll)b);
		}
	}
}

[BZOJ3786]星系探索

标签:一点   为什么   减法   define   前缀和   for   chain   clu   size   

原文地址:http://www.cnblogs.com/jefflyy/p/7532420.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!