BZOJ 2049 [Sdoi2008]Cave 洞穴勘测 LCT

题意:链接

方法: LCT

解析:

搞了一下午的LCT,这道题就当做第一道模板?题。然后大概写个理解总结什么的。

首先!splay不要写挂!不要写挂!

然后对于这道题。没有什么奇怪的操作。

只有两个操作,将两个节点连起来,将两个节点之间的连边断开。

每一次询问,询问两个节点是否连通。

听起来挺简单的,一下子就想到了并查集有没有!

然而发现并查集并不可以搞。

也许是我太弱,但是我真的不会并查集的分割。

所以还是老老实实来想LCT。

首先我们定义几个函数。

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);
    }
}

你可能感兴趣的:(BZOJ 2049 [Sdoi2008]Cave 洞穴勘测 LCT)