【洛谷P4219】 [BJOI2014]大融合【LCT】

传送门

Solution

首先边\((x,y)\)的负载显然可以转化为,\(x,y\)所在树中,(\(x\)的子树大小\(-y\)的子树大小)\(*y\)的子树大小(假设\(x\)\(y\)的父亲)

我们想用\(LCT\)来维护这样的东西,但之前我们提到过,\(LCT\)维护子树信息十分困难,但子树和这样的也是比较好处理的:

显然子树和可以由实子树和和虚子树和组成,于是考虑用\(sum1\)维护子树和,\(sum2\)维护虚子树和,那么\(pushup\)就很好写了:

inline void pushup(int x){
	sum1[x]=sum1[ch[x][0]]+sum1[ch[x][1]]+1+sum2[x];
}

接下来考虑我们什么时候改变了\(sum2\),其实只有两个地方:

\(1.access\)

\(access\)过程中,我们将每个点\(x\)的右儿子改变,\(sum2\)自然就会增加原来右儿子的子树和,减去新的右儿子的子树和:

inline void access(int x){
	for(int last=0;x;last=x,x=fa[x]){
		Splay(x);
		sum2[x]+=sum1[ch[x][1]];
		sum2[x]-=sum1[last];
		ch[x][1]=last;
		pushup(x);
	}
}

\(2.link\)

\(link\)过程中,我们在\((x,y)\)间连了一条轻边,于是要在\(y\)\(sum2\)中增加\(x\)所在树的点权和:

inline void link(int x,int y){
	split(x,y); //split其实就是完成了正常link的makeroot操作并且将x到y的链找出来
	sum2[y]+=sum1[x]; 
	fa[x]=y;
}

其它的就是模板了.

Code

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
int n,sum1[N],ch[N][2],fa[N],rev[N],sum2[N];//sum1为所有子树siz之和,sum2为虚子树siz之和 
inline bool which(int x){return ch[fa[x]][1]==x;}
inline void pushup(int x){
	sum1[x]=sum1[ch[x][0]]+sum1[ch[x][1]]+1+sum2[x];
}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void rever(int x){
	swap(ch[x][0],ch[x][1]);
	rev[x]^=1;
}
inline void pushdown(int x){
	if(!rev[x]) return ;
	if(ch[x][0]) rever(ch[x][0]);
	if(ch[x][1]) rever(ch[x][1]);
	rev[x]=0;
}
inline void Rotate(int x){
	int y=fa[x],z=fa[y],wx=which(x),wy=which(y);
	if(!isroot(y)) ch[z][wy]=x;fa[x]=z;
	ch[y][wx]=ch[x][wx^1];
	if(ch[y][wx]) fa[ch[y][wx]]=y;
	ch[x][wx^1]=y;fa[y]=x;
	pushup(y);
}
int stk[N],top;
inline void Splay(int x){
	int now=x;
	stk[top=1]=now;
	while(!isroot(now)) now=fa[now],stk[++top]=now;
	while(top) pushdown(stk[top--]);
	while(!isroot(x)){
		int y=fa[x];
		if(!isroot(y)){
			if(which(x)==which(y)) Rotate(y);
			else Rotate(x);
		}Rotate(x);
	}
	pushup(x); 
} 
inline void access(int x){
	for(int last=0;x;last=x,x=fa[x]){
		Splay(x);
		sum2[x]+=sum1[ch[x][1]];
		sum2[x]-=sum1[last];
		ch[x][1]=last;
		pushup(x);
	}
}
inline void makeroot(int x){
	access(x);Splay(x);
	rever(x);
}
inline void split(int x,int y){
	makeroot(x);
	access(y);Splay(y);
}
inline void link(int x,int y){
	split(x,y); 
	sum2[y]+=sum1[x]; 
	fa[x]=y;
}
int q;
char op[10];
int main(){
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;++i) sum1[i]=1;
	while(q--){
		int x,y;
		scanf("%s%d%d",op,&x,&y);
		if(op[0]=='A') link(x,y);
		else makeroot(y),access(x),Splay(y),printf("%lld\n",1ll*(sum1[y]-sum1[x])*(sum1[x]));	
	}
	return 0;
}
posted @ 2021-01-06 20:41  cjTQX  阅读(81)  评论(0)    收藏  举报