P4219 [BJOI2014]大融合 LCT维护子树信息

题意:

戳这里

分析:

我们发现题意就是求一条边所连两个点各自的子树大小的乘积

但是关键问题来了,LCT 的本质是维护链的信息,那么怎么维护子树的信息呢?

常见操作就是新开一个数组用来记录子树信息( (siz,val) 等)

但是在 (link,access,cut) 这类修改 (splay) 形态的操作中,我们需要手动更改实虚儿子变化带来的影响,然后让影响 (pushup) 上去就可以了

  • tip: (link) 里面原来的 (makeroot) 需要改成 (split) (等价于 (makeroot,access,splay)) 因为需要修改对 (y) 的影响所以要把 (y) 旋上去

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	int read()
	{
		int x=0,f=1;char ch=getchar();
		while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
		while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	const int maxn = 1e5+5;
	int n,m,a,b;
	char opt[5];
	namespace lct
	{
		int top,q[maxn],c[maxn][2],fa[maxn],siz[maxn],rev[maxn],val[maxn];
		void pushup(int x){siz[x]=siz[c[x][0]]+siz[c[x][1]]+val[x]+1;}
		void pushdown(int x)
		{
			int l=c[x][0],r=c[x][1];
			if(rev[x])
			{
				rev[l]^=1;rev[r]^=1;rev[x]^=1;
				swap(c[x][0],c[x][1]);
			}
		}
		bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
		void rotate(int x)
		{
			int y=fa[x],z=fa[y],l,r;
			if(c[y][0]==x)l=0;else l=1;r=l^1;
			if(!isroot(y)){if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;}
			fa[y]=x;fa[x]=z;fa[c[x][r]]=y;
			c[y][l]=c[x][r];c[x][r]=y;
			pushup(y);pushup(x);
		}
		void splay(int x)
		{
			top=1;q[top]=x;
			for(int i=x;!isroot(i);i=fa[i]) q[++top]=fa[i];
			for(int i=top;i;i--) pushdown(q[i]);
			while(!isroot(x))
			{
				int y=fa[x],z=fa[y];
				if(!isroot(y))
				{
					if((c[z][0]==y)^(c[y][0]==x)) rotate(x);
					else rotate(y);
				}
				rotate(x);
			}
		}
		void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),val[x]+=siz[c[x][1]]-siz[c[x][1]=t],pushup(x);}
		void makeroot(int x){access(x);splay(x);rev[x]^=1;}
		int find(int x){access(x);splay(x);while(c[x][0])x=c[x][0];splay(x);return x;}
		void split(int x,int y){makeroot(x);access(y);splay(y);}
		void cut(int x,int y){split(x,y);if(c[y][0]==x&&c[x][1]==0)fa[x]=0,c[y][0]=0,pushup(y);}
		void link(int x,int y){split(x,y);fa[x]=y;val[y]+=siz[x];pushup(y);}
	}
	using namespace lct;
	
	void work()
	{
		n=read();m=read();
		for(int i=1;i<=m;i++)
		{
			scanf("%s",opt);a=read();b=read();
			if(opt[0]=='A') link(a,b);
			else
			{
				split(a,b);
				printf("%lld
",1ll*(val[a]+1)*(val[b]+1));
			}
		}
	}
	
}

int main()
{
	zzc::work();
	return 0;
}
原文地址:https://www.cnblogs.com/youth518/p/14111504.html