BZOJ 2049 [Sdoi2008]Cave 洞穴勘测

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=2049

 

[分析]

  这题仍然也是不需要转化模型的单纯维护森林的问题 [因为两点之间至多一条路径嘛].

  所以仍然是考验LCT的代码与重要函数的实现.

  

  这次主要进行的操作多了Cut(u,v)\Link(u,v)\Query(u,v)

  分别作用是删除边(u,v),连接边(u,v),判断两点是否在同一棵树中[在同一棵树中 <=> 两点间有通路]

  首先说一个新的名叫make_root(x)的辅助性操作,指让x成为x所在树的根

    就是先Access(x),使其与根连通,而且是所在链上深度最大的,然后在所在splay上打下翻转tag,它就成了根

  删除边稍微难想一些:

  

  添边:

    为了满足要求,先将u变成所在树的树根,然后再连上v就好了

  询问:

    直接向上回溯...没什么好说的.

  

  这样说应该有些抽象,具体欢迎看代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=10010;

inline int in(){
    int x=0;char ch=getchar();
    while(ch>'9' || ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}

char ord[8];

struct Node{
    int l,r,f;
    bool rt,rv;
    
    void trans(){ swap(l,r);}
}s[maxn];

void push_down(int x){
    if(s[x].rv){
        s[x].trans();
        if(s[x].l) s[s[x].l].rv^=1;
        if(s[x].r) s[s[x].r].rv^=1;
        s[x].rv=0;
    }
}

void down_tag(int x){
    if(!s[x].rt) down_tag(s[x].f);
    push_down(x);
}

void zig(int x){
    int y=s[x].f;
    s[x].f=s[y].f;
    if(s[y].rt) s[y].rt=false,s[x].rt=true;
    else{ if(y==s[s[y].f].l) s[s[y].f].l=x;
        else s[s[y].f].r=x;}
    s[y].l=s[x].r;
    if(s[x].r) s[s[x].r].f=y;
    s[x].r=y,s[y].f=x;
}

void zag(int x){
    int y=s[x].f;
    s[x].f=s[y].f;
    if(s[y].rt) s[y].rt=false,s[x].rt=true;
    else{ if(y==s[s[y].f].l) s[s[y].f].l=x;
        else s[s[y].f].r=x;}
    s[y].r=s[x].l;
    if(s[x].l) s[s[x].l].f=y;
    s[x].l=y,s[y].f=x;
}

void Splay(int x){
    down_tag(x);
    int y;
    while(!s[x].rt){
        y=s[x].f;
        if(s[y].rt){ if(x==s[y].l) zig(x);
            else zag(x);}
        else{
            int z=s[y].f;
            if(y==s[z].l){ if(x==s[y].l) zig(y),zig(x);
                else zag(x),zig(x);}
            else { if(x==s[y].r) zag(y),zag(x);
                else zig(x),zag(x);}
        }
    }
}

void Access(int x){
    for(int last=0;x;x=s[last=x].f){
        Splay(x);
        s[s[x].r].rt=true;
        s[x].r=last;
        s[last].rt=false;
    }
}

/*
    make_rt(x)操作表示将x作为x所在树的根
    当Access和Splay操作完后,这个点处于平衡树的顶端而且没有右儿子[切断了联系]
    那么将正棵平衡树翻转一下就让x作为正棵树的最左端点了[没有左儿子]
*/

void make_rt(int x){
    Access(x);
    Splay(x);
    s[x].rv^=1;
}

bool query(int u,int v){
    while(s[u].f) u=s[u].f;
    while(s[v].f) v=s[v].f;
    return u==v;
}

void Link(int u,int v){
    make_rt(u);
    s[u].f=v;
}

void Cut(int u,int v){
    make_rt(u);
    Access(v);
    Splay(v);
    s[s[v].l].f=s[v].f;
    s[s[v].l].rt=true;
    s[v].f=s[v].l=0;
}

int n,m;

int main(){
#ifndef ONLINE_JUDGE
    freopen("2049.in","r",stdin);
    freopen("2049.out","w",stdout);
#endif

    int u,v;

    n=in(),m=in();
    for(int i=1;i<=n;i++) s[i].rt=true;
    while(m--){
        scanf("%s",ord);u=in(),v=in();
        if(ord[0]=='Q'){
            if(query(u,v)) puts("Yes");
            else puts("No");
        }
        else if(ord[0]=='C')
            Link(u,v);
        else
            Cut(u,v);
    }

    return 0;
}
View Code

 

posted @ 2016-01-13 21:47  诚叙  阅读(...)  评论(... 编辑 收藏