大融合BJOI2014 洛谷4219 LCT (LCT维护子树信息学习笔记)

题目链接

这题也是bzoj的题,题号似乎是bzoj4530,但是我没有权限号,所以没有在bzoj上提交,所以不知道能不能过。

题解:

LCT维护子树信息。

这篇顺便就当我的LCT维护子树信息的学习笔记好了。

我们知道,树剖的比较容易维护子树信息的,但是我之前一直以为LCT是不可以维护子树信息的,因为根据LCT的性质,它会把子树变为实子树(实边连接的)和虚子树(虚边连接的),所以我以为只能维护链上信息,觉得子树信息不可维护。学习之后发现,之前很多人说的树剖能做的LCT都能做暂时又变成了对的。。。

方法如下:

我们用数组s表示实子树+虚子树信息,si表示虚子树上维护的信息。我们考虑LCT的每一个操作对这两个需要维护的量的影响。

我们发现,会影响si的只有link操作和access操作。下面我们分别来分析一下。

对于link(x,y)操作,我们让x接到y上,相当于y多了一个虚儿子,所以用s[x]的信息更新si[y],然后pushup(y)来更新一下s[y]。当然,在这之前,我们makeroot(x)之后需要先access(y)+splay(y),据说是因为y不一定是原splay的根,不这样写的话原来的splay中y的祖先都没法被更新到。

对于access操作,我们在把它与根的路径连通时,si的变化的减去原来右儿子的s,加上新的右儿子的s。

然后就没什么难度了,查询其实就相当于是找x虚子树大小加上它自己与y的虚子树加上y本身的乘积,其实好像(y是根)s[x]*(si[y]+1)也是对的(注意开long long)。

最后,据说维护最值要对每个节点维护一个splay,我暂时不会。

下面是代码:

#include 
using namespace std;

int q,n,f[100010],c[100010][2],rev[100010],st[100010],s[100010],si[100010];
//s为实子树与虚子树总和,si为虚子树总和
int nroot(int x)
{
	return c[f[x]][0]==x||c[f[x]][1]==x;
}
void pushup(int x)
{
	s[x]=s[c[x][0]]+s[c[x][1]]+1+si[x];
}
void pushdown(int x)
{
	if(rev[x])
	{
		swap(c[x][0],c[x][1]);
		rev[c[x][0]]^=1;
		rev[c[x][1]]^=1;
		rev[x]=0;
	}
}
void rotate(int x)
{
	int y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
	if(nroot(y))
	c[z][c[z][1]==y]=x;
	c[x][!k]=y;
	c[y][k]=w;
	if(w)
	f[w]=y;
	f[y]=x;
	f[x]=z;
	pushup(y);
	pushup(x);
}
void splay(int x)
{
	int y=x,z=0;
	st[++z]=y;
	while(nroot(y))
	{
		y=f[y];
		st[++z]=y;
	}
	while(z)
	pushdown(st[z--]);
	while(nroot(x))
	{
		y=f[x],z=f[y];
		if(nroot(y))
		{
			if(c[z][0]==y ^ c[y][0]==x)
			rotate(y);
			else
			rotate(x);
		}
		rotate(x);
	}
	pushup(x);
} 
void access(int x)
{
	int y=0;
	while(x)
	{
		splay(x);		
		si[x]+=s[c[x][1]];
		si[x]-=s[y];
		c[x][1]=y;
		y=x;
		x=f[x];
//不用pushup,因为access对总和s是没用影响的 
	}
}
void makeroot(int x)
{
	access(x);
	splay(x);
	rev[x]^=1;
}
void link(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
	f[x]=y;
	si[y]+=s[x];
	pushup(y);
}
int main()
{
	scanf("%d%d",&n,&q);
	char opt;
	int x,y;
	for(int i=1;i<=n;++i)
	s[i]=1;
	for(int i=1;i<=q;++i)
	{
		opt=getchar();
		if(opt!='A'&&opt!='Q')
		opt=getchar();
		scanf("%d%d",&x,&y);
		if(opt=='A')
		link(x,y);
		else
		{
			makeroot(x);
			access(y);
			splay(y);
			printf("%lld\n",(long long)(si[x]+1)*(si[y]+1));
		}
	}
	return 0;
}

你可能感兴趣的:(LCT,数据结构,学习笔记)