BZOJ2049 [Sdoi2008]Cave 洞穴勘测 LCT

欢迎访问~原文出处——安科网-zhouzhendong

去安科网看该题解


题目传送门 - BZOJ2049


题意概括

有一堆点,一开始没有连边。

有3种操作,一种是连接某两个点,一种是断开某一条边。还有一种是询问两个点是否连通。

操作过程中保证整个图是森林。

点数<=10000,操作数<=200000


题解

LCT板子题。

对于询问,我们只需要access一下,然后splay一下,然后比较所在连通块的最左位置就可以了。


代码

#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=10005;
int n,m,fa[N],son[N][2],size[N],rev[N];
void pushup(int x){
	size[x]=size[son[x][0]]+size[son[x][1]]+1;
}
bool isroot(int x){
	return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
}
void pushdown(int x){
	if (rev[x]){
		rev[x]=0;
		rev[son[x][0]]^=1;
		rev[son[x][1]]^=1;
		swap(son[x][0],son[x][1]);
	}
}
void pushadd(int x){
	if (!isroot(x))
		pushadd(fa[x]);
	pushdown(x);
}
int wson(int x){
	return son[fa[x]][1]==x;
}
void rotate(int x){
	if (isroot(x))
		return;
	int y=fa[x],z=fa[y],L=wson(x),R=L^1;
	if (!isroot(y))
		son[z][wson(y)]=x;
	fa[x]=z;
	fa[y]=x;
	fa[son[x][R]]=y;
	son[y][L]=son[x][R];
	son[x][R]=y;
	pushup(y);
	pushup(x);
}
void splay(int x){
	pushadd(x);
	for (int y=fa[x];!isroot(x);rotate(x),y=fa[x])
		if (!isroot(y))
			rotate(wson(x)==wson(y)?y:x);
}
void access(int x){
	int t=0;
	while (x){
		splay(x);
		son[x][1]=t;
		t=x;
		x=fa[x];
	}
}
void rever(int x){
	access(x);
	splay(x);
	rev[x]^=1;
}
void link(int x,int y){
	rever(x);
	fa[x]=y;
}
void cut(int x,int y){
	rever(x);
	access(y);
	splay(y);
	fa[x]=son[y][0]=0;
}
int find(int x){
	access(x);
	splay(x);
	while (son[x][0])
		x=son[x][0];
	return x;
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		size[i]=1,rev[i]=fa[i]=son[i][0]=son[i][1]=0;
	while (m--){
		char op[10];
		int a,b;
		scanf("%s%d%d",op,&a,&b);
		if (op[0]=='C')
			link(a,b);
		else if (op[0]=='D')
			cut(a,b);
		else
			puts(find(a)==find(b)?"Yes":"No");
	}
	return 0;
}

相关推荐