【洛谷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
",1ll*(sum1[y]-sum1[x])*(sum1[x]));	
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tqxboomzero/p/14243346.html