题意:链接
方法: LCT
解析:
搞了一下午的LCT,这道题就当做第一道模板?题。然后大概写个理解总结什么的。
首先!splay不要写挂!不要写挂!
然后对于这道题。没有什么奇怪的操作。
只有两个操作,将两个节点连起来,将两个节点之间的连边断开。
每一次询问,询问两个节点是否连通。
听起来挺简单的,一下子就想到了并查集有没有!
然而发现并查集并不可以搞。
也许是我太弱,但是我真的不会并查集的分割。
所以还是老老实实来想LCT。
LCT 顾名思义,Link Cut 是其比较有代表性的操作?
首先我们定义几个函数。
access函数,代表的是从某个节点一直访问到其所在的splay的树根。并且将这一段链看作重链?
然后是splay,这就不说了。
movetoroot函数。
代表的是换根。也就是将x所在的splay里的根换为x。
这个函数怎么实现呢?
显然应该先访问该节点,确定好我们定义的重链。
之后将x旋到根。假设我们以深度为关键字。那我们旋转之后。该节点与树根之间的父子关系发生了对调。在以深度为关键字的splay tree中的体现就是所有的左右儿子对调,所以我们只要打一个reverse标记就可以了。
cut函数。
首先呢。我们要判断cut的两个节点(x,y)是否在同一棵splay tree里。
如果不在直接return。
然后我们把根换为x,访问以下y,之后把y旋到根。
这样的话。由于splay里是以深度为关键字。所以x一定在y的左儿子。
直接将二者连边干掉就行了。
link函数
把根换为x,然后直接令x的父亲为y就行了。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 10010
using namespace std;
int ch[N][2];
int fa[N];
int rev[N];
int rt[N];
int n,m;
void reverse(int x)
{
if(!x)return;
rev[x]^=1;
swap(ch[x][0],ch[x][1]);
}
void pushdown(int x)
{
if(rev[x])
{
reverse(ch[x][0]);
reverse(ch[x][1]);
rev[x]=0;
}
}
void down(int x)
{
if(!rt[x])down(fa[x]);
pushdown(x);
}
void pushup(int x){}
void rotate(int x)
{
int y=fa[x],kind=ch[y][1]==x;
ch[y][kind]=ch[x][!kind];
fa[ch[y][kind]]=y;
fa[x]=fa[y];
fa[y]=x;
ch[x][!kind]=y;
if(rt[y])
{
rt[x]=1,rt[y]=0;
}else ch[fa[x]][ch[fa[x]][1]==y]=x;
pushup(y);
}
void splay(int x)
{
down(x);
while(!rt[x])
{
int y=fa[x],z=fa[y];
if(rt[y])
{
rotate(x);
}else if((ch[y][1]==x)==(ch[z][1]==y))
{
rotate(y),rotate(x);
}else
{
rotate(x),rotate(x);
}
}
pushup(x);
}
void access(int x)
{
int y=0;
while(x)
{
splay(x);
rt[ch[x][1]]=1;rt[y]=0;
ch[x][1]=y;
pushup(x);
y=x,x=fa[x];
}
}
int find_root(int x)
{
while(fa[x])x=fa[x];
return x;
}
void mt(int x)
{
access(x);
splay(x);
reverse(x);
}
void link(int x,int y)
{
mt(x);
fa[x]=y;
}
void cut(int x,int y)
{
mt(x);
access(y);
splay(y);
fa[x]=0;
ch[y][0]=0;
rt[x]=1;
}
void init(int x)
{
fa[x]=ch[x][0]=ch[x][1]=rev[x]=0;
rt[x]=1;
}
char opt[25];
int main()
{
freopen("cave.in","r",stdin);
freopen("cave.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)init(i);
for(int i=1;i<=m;i++)
{
scanf("%s",opt);
int x,y;
scanf("%d%d",&x,&y);
if(opt[0]=='Q')
{
if(find_root(x)==find_root(y))puts("Yes");
else puts("No");
}else if(opt[0]=='D')cut(x,y);
else link(x,y);
}
}