SPOJ16549 BZOJ3637 QTREE6 - Query on a tree VI LCT

题目链接

题意:
给你一棵 n n 个点的树,每个点有一个颜色,是黑色或者白色,一开始所有的点都是白色。有 m m 次操作,有两种操作,第一种是把一个点的颜色黑白翻转,第二种操作是给你一个 x x ,询问有多少个 u u 满足到 x x 的路径上的所有点颜色都相同,也就是问 x x 所在的同色连通块的大小。 n , m < = 1 e 5 n,m<=1e5

题解:
又不会,抄了y_immortal神仙的博客,还听了他这个神人的讲解才会的。又被他吊锤了,QAQ。好久没写LCT了,最近写两道练一练。本篇博客几乎是转载y_immortal神犇的。这个这个神仙他允许我转他的了。

这个题的思路还是很巧妙的。

发现查询的信息不是关于黑色或者白色,而是相同颜色,所以不能仅仅用一颗树来做了

那么考虑建立黑白两棵树。

一个比较暴力的思想就是每次换颜色,然后暴力的断掉目前的所有边,然后和与翻转颜色之后 与之相同的点进行连边,但是这样的方法在菊花图的时候是不对的。我自己在想这个题的时候有好几种想法都不能解决在树是菊花树时的link和cut。

一个非常巧妙的思路,将颜色从点转到边上,也就是说我们以 1 1 为根的话,若当前在黑色树中存在 i f a [ i ] i→fa[i] 的边的话,那么就说明这个点是黑色的。特别的,由于 1 1 号点没有父亲,所以我们要建虚点 n + 1 n+1 作为它的父亲,这样做的好处在下面会介绍。这个思路比较特别,可能我还是太年轻,之前我还没有见过这种做法的题目。

那么这样每次修改的话,只会断一条边,并连接一条边。
但是由于我们为了写起来更简便一些,所以不要去makeroot,因为我们要始终以深度最小的点为根,所以你就算link的时候makeroo了,最后只要makeroot回来就可以,但是找这个点比较复杂,所以还是不要makeroot了。我来解释一下,与原来的区别是我们以前makeroot函数最后有一个reverse,而这里我们只可以用access和splay函数,不去reverse,因为reverse会改变LCT上的父子关系因为要保证LCT上的父子关系与原树相同,这样才能比较方便回答询问。

那么考虑该如何计算答案呢,我们会发现,若最顶上的那个点颜色不同的话,要输出这个点所在子树的size,否则就是整个连通块的size,我们惊奇的发现,由于我们的 1 1 号点也有一个虚点父亲,所以我们每次access(x),然后splay那个深度最小的点,可以通过直接询问根节点右儿子的size,这里就不做详细的解释了。这个东西感受一下就挺对的吧。
因为两种情况都应该回答这个东西。

注意由于是维护子树信息,所以要维护虚儿子的信息。
最后输出的也应该是虚+实。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,hed[200010],cnt,fa[200010],num,col[200010];
struct edge
{
	int to,next;
}a[400010];
struct node
{
	int c[200010][2],f[200010],sz[200010],rev[200010],xu[200010];
	inline int nroot(int x)
	{
		return c[f[x]][0]==x||c[f[x]][1]==x;
	}
	inline void pushup(int x)
	{
		sz[x]=sz[c[x][0]]+sz[c[x][1]]+xu[x]+1;
	}
	inline 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[y][k]=w;
		c[x][!k]=y;
		if(w)
		f[w]=y;
		f[x]=z;
		f[y]=x;
		pushup(y);
		pushup(x);
	}
	inline void splay(int x)
	{
		while(nroot(x))
		{
			int y=f[x],z=f[y];
			if(nroot(y))
			{
				if(c[y][1]==x^c[z][1]==y)
				rotate(x);
				else
				rotate(y);
			}
			rotate(x);
		}
		pushup(x);
	}
	inline void access(int x)
	{
		int y=0;
		while(x!=0)
		{
			splay(x);
			xu[x]+=sz[c[x][1]]-sz[y];
			c[x][1]=y;
			if(y)
			f[y]=x;
			pushup(x);
			y=x;			
			x=f[x];			
		}
	}
	inline void link(int x,int y)
	{
		access(x);
		splay(x);
		access(y);
		splay(y);
		f[x]=y;
		xu[y]+=sz[x];
		pushup(y);
	}
	inline void cut(int x)
	{
		access(x);
		splay(x);
		f[c[x][0]]=0;
		c[x][0]=0;
	}
	inline int find(int x)
	{
		access(x);
		splay(x);
		while(c[x][0])
		x=c[x][0];
		return x;
	}
}b,w;
inline int read()
{
	int x=0;
	char s=getchar();
	while(s>'9'||s<'0')
	s=getchar();
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x;
}
inline void add(int from,int to)
{
	a[++cnt].to=to;
	a[cnt].next=hed[from];
	hed[from]=cnt;
}
inline void dfs(int x)
{
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(y==fa[x])
		continue;
		fa[y]=x;
		dfs(y);
	}
}
int main()
{
	n=read();
	for(int i=1;i<=n-1;++i)
	{
		int x=read(),y=read();
		add(x,y);
		add(y,x);
	}
	fa[1]=n+1;
	dfs(1);
	b.sz[n+1]=1;
	w.sz[n+1]=1;
	for(int i=1;i<=n;++i)
	{
		b.sz[i]=1;
		w.sz[i]=1;
		w.link(i,fa[i]);		
	}	
	m=read();
	for(int i=1;i<=m;++i)
	{
		int opt=read(),x=read();
		if(opt==0)
		{
			if(col[x])
			{				
				int y=b.find(x);
				b.access(x);
				b.splay(y);
				printf("%d\n",b.sz[b.c[y][1]]);
			}	
			else
			{
				int y=w.find(x);
				w.access(x);
				w.splay(y);
				printf("%d\n",w.sz[w.c[y][1]]);
			}
		}
		else
		{
			if(col[x])
			{
				col[x]=0;
				b.cut(x);
				w.link(x,fa[x]);
			}
			else
			{
				col[x]=1;
				w.cut(x);
				b.link(x,fa[x]);
			}
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/88706150