【BZOJ4530】[Bjoi2014]大融合 LCT维护子树信息

【BZOJ4530】[Bjoi2014]大融合

Description

小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。
这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。
例如,在上图中,现在一共有了5条边。其中,(3,8)这条边的负载是6,因为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8)。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问。

Input

第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
接下来的Q行,每行是如下两种格式之一:
A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。
1≤N,Q≤100000

Output

对每个查询操作,输出被查询的边的负载。

Sample Input

8 6
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8

Sample Output

6

题解:网上看到好多并查集+线段树合并,感觉想法很好,但是还是去学了一发姜神的LCT。

用LCT维护子树信息,维护LCT中一个点的所有儿子的信息以及所有虚儿子的信息。因为当我们对x进行access+splay操作后,原树中x的父亲全在splay中x的左子树里,并且x没有右子树,所以x的儿子都在x的虚子树中。那么如何维护虚子树的信息呢?

发现,虚子树的信息只有在access和link时会变化,在access时,原来的右儿子变虚,又新来了一个右儿子,所以虚子树的信息要加上原来的右儿子,减去新加的右儿子。在link时,y新加了虚儿子x,那么y要加上x的信息。并且注意,为了不影响其他的点,需要对y也进行access+splay操作。至于所有实+虚儿子的信息,直接pushup就行了。

答案就是:将y变成根,对x进行access+splay,然后ans=(y的子树信息-x的虚子树信息-1)*(x的虚子树信息+1)

 

#include 
#include 
#include 
using namespace std;
const int maxn=100010;
struct LCT
{
	int rev[maxn],sx[maxn],sl[maxn],ch[maxn][2],fa[maxn];
	bool isr(int x)	{return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
	void pushup(int x)	{sl[x]=sx[x]+sl[ch[x][0]]+sl[ch[x][1]]+1;}
	void pushdown(int x)
	{
		if(rev[x])
		{
			swap(ch[x][0],ch[x][1]);
			if(ch[x][0])	rev[ch[x][0]]^=1;
			if(ch[x][1])	rev[ch[x][1]]^=1;
			rev[x]=0;
		}
	}
	void updata(int x)
	{
		if(!isr(x))	updata(fa[x]);
		pushdown(x);
	}
	void rotate(int x)
	{
		int y=fa[x],z=fa[y],d=(x==ch[y][1]);
		if(!isr(y))	ch[z][y==ch[z][1]]=x;
		fa[x]=z,fa[y]=x,ch[y][d]=ch[x][d^1];
		if(ch[x][d^1])	fa[ch[x][d^1]]=y;
		ch[x][d^1]=y;
		pushup(y),pushup(x);
	}
	void splay(int x)
	{
		updata(x);
		while(!isr(x))
		{
			int y=fa[x],z=fa[y];
			if(!isr(y))
			{
				if((x==fa[y])^(z==fa[y]))	rotate(x);
				else	rotate(y);
			}
			rotate(x);
		}
	}
	void access(int x)
	{
		for(int y=0;x;splay(x),sx[x]+=sl[ch[x][1]],ch[x][1]=y,sx[x]-=sl[y],pushup(x),y=x,x=fa[x]);
	}
	void maker(int x)
	{
		access(x),splay(x),rev[x]^=1;
	}
	void link(int x,int y)
	{
		maker(x),access(y),splay(y);
		sx[y]+=sl[x],fa[x]=y,pushup(y);
	}
}tr;
int n,m;
char str[5];
int main()
{
	int i,a,b;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)	tr.sl[i]=1;
	for(i=1;i<=m;i++)
	{
		scanf("%s%d%d",str,&a,&b);
		if(str[0]=='A')	tr.link(a,b);
		else
		{
			tr.maker(b),tr.access(a),tr.splay(a);
			printf("%lld\n",(long long)(tr.sl[a]-tr.sx[b]-1)*(tr.sx[b]+1));
		}
	}
	return 0;
}

 

转载于:https://www.cnblogs.com/CQzhangyu/p/7061181.html

你可能感兴趣的:(【BZOJ4530】[Bjoi2014]大融合 LCT维护子树信息)