LCT(模板题)BZOJ2049

2049: [Sdoi2008]Cave 洞穴勘测

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 3769   Solved: 1680
[ Submit][ Status][ Discuss]

Description

辉辉热衷于洞穴勘测。某天,他按照地图来到了一片被标记为JSZX的洞穴群地区。经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴。假如两个洞穴可以通过一条或者多条通道按一定顺序连接起来,那么这两个洞穴就是连通的,按顺序连接在一起的这些通道则被称之为这两个洞穴之间的一条路径。洞穴都十分坚固无法破坏,然而通道不太稳定,时常因为外界影响而发生改变,比如,根据有关仪器的监测结果,123号洞穴和127号洞穴之间有时会出现一条通道,有时这条通道又会因为某种稀奇古怪的原因被毁。辉辉有一台监测仪器可以实时将通道的每一次改变状况在辉辉手边的终端机上显示:如果监测到洞穴u和洞穴v之间出现了一条通道,终端机上会显示一条指令 Connect u v 如果监测到洞穴u和洞穴v之间的通道被毁,终端机上会显示一条指令 Destroy u v 经过长期的艰苦卓绝的手工推算,辉辉发现一个奇怪的现象:无论通道怎么改变,任意时刻任意两个洞穴之间至多只有一条路径。因而,辉辉坚信这是由于某种本质规律的支配导致的。因而,辉辉更加夜以继日地坚守在终端机之前,试图通过通道的改变情况来研究这条本质规律。然而,终于有一天,辉辉在堆积成山的演算纸中崩溃了……他把终端机往地面一砸(终端机也足够坚固无法破坏),转而求助于你,说道:“你老兄把这程序写写吧”。辉辉希望能随时通过终端机发出指令 Query u v,向监测仪询问此时洞穴u和洞穴v是否连通。现在你要为他编写程序回答每一次询问。已知在第一条指令显示之前,JSZX洞穴群中没有任何通道存在。

Input

第一行为两个正整数n和m,分别表示洞穴的个数和终端机上出现过的指令的个数。以下m行,依次表示终端机上出现的各条指令。每行开头是一个表示指令种类的字符串s("Connect”、”Destroy”或者”Query”,区分大小写),之后有两个整数u和v (1≤u, v≤n且u≠v) 分别表示两个洞穴的编号。

Output

对每个Query指令,输出洞穴u和洞穴v是否互相连通:是输出”Yes”,否则输出”No”。(不含双引号)

Sample Input

样例输入1 cave.in
200 5
Query 123 127
Connect 123 127
Query 123 127
Destroy 127 123
Query 123 127
样例输入2 cave.in

3 5
Connect 1 2
Connect 3 1
Query 2 3
Destroy 1 3
Query 2 3



Sample Output

样例输出1 cave.out
No
Yes
No


样例输出2 cave.out

Yes
No

HINT

数据说明 10%的数据满足n≤1000, m≤20000 20%的数据满足n≤2000, m≤40000 30%的数据满足n≤3000, m≤60000 40%的数据满足n≤4000, m≤80000 50%的数据满足n≤5000, m≤100000 60%的数据满足n≤6000, m≤120000 70%的数据满足n≤7000, m≤140000 80%的数据满足n≤8000, m≤160000 90%的数据满足n≤9000, m≤180000 100%的数据满足n≤10000, m≤200000 保证所有Destroy指令将摧毁的是一条存在的通道本题输入、输出规模比较大,建议c\c++选手使用scanf和printf进行I\O操作以免超时

被注释掉的是数组版:

//#include<iostream>
//#include<cstdio>
//#include<string>
//#include<cstring>
//#include<vector>
//#include<cmath>
//#include<queue>
//#include<stack>
//#include<map>
//#include<set>
//#include<algorithm>
//using namespace std;
//const int maxn=300010;
//const int INF=1000000000;
//int N,Q;
//struct Edge
//{
//    int v,next;
//}edge[maxn*2];
//int head[maxn],tot;
//struct LCT
//{
//    int ch[maxn][2],pre[maxn],key[maxn];
//    int rev[maxn];
//    bool rt[maxn];
//    void add_edge(int u,int v)
//    {
//        edge[tot].v=v;
//        edge[tot].next=head[u];
//        head[u]=tot++;
//    }
//    void dfs(int u)
//    {
//        for(int i=head[u];i!=-1;i=edge[i].next)
//        {
//            int v=edge[i].v;
//            if(pre[v]!=0)continue;
//            pre[v]=u;
//            dfs(v);
//        }
//    }
//    void update_rev(int r)
//    {
//        if(!r)return;
//        swap(ch[r][0],ch[r][1]);
//        rev[r]^=1;
//    }
//    void pushdown(int r)
//    {
//        if(rev[r])
//        {
//            update_rev(ch[r][1]);
//            update_rev(ch[r][0]);
//            rev[r]=0;
//        }
//    }
//    void rotate(int x)
//    {
//        int y=pre[x],kind=ch[y][1]==x;
//        ch[y][kind]=ch[x][!kind];
//        pre[ch[y][kind]]=y;
//        pre[x]=pre[y];
//        pre[y]=x;
//        ch[x][!kind]=y;
//        if(rt[y])rt[y]=false,rt[x]=true;
//        else ch[pre[x]][ch[pre[x]][1]==y]=x;
//    }
//    //将根节点到r的路径上的所有及诶单的标记下方
//    void P(int r)
//    {
//        if(!rt[r])P(pre[r]);
//        pushdown(r);
//    }
//    void Splay(int r)
//    {
//        P(r);
//        while(!rt[r])
//        {
//            int f=pre[r],ff=pre[f];
//            if(rt[f])rotate(r);
//            else if((ch[ff][1]==f)==(ch[f][1]==r))
//                rotate(f),rotate(r);
//            else rotate(r),rotate(r);
//        }
//    }
//    int Access(int x)
//    {
//        int y=0;
//        for(;x;x=pre[y=x])
//        {
//            Splay(x);
//            rt[ch[x][1]]=true,rt[ch[x][1]=y]=false;
//
//        }
//        return y;
//    }
//     int getroot(int x)
//     {
//        Access(x);
//        Splay(x);
//        while (ch[x][0])
//            x = ch[x][0];
//        return x;
//    }
//    //判断是否同根
//    bool judge(int u,int v)
//    {
//        while(pre[u])u=pre[u];
//        while(pre[v])v=pre[v];
//        return u==v;
//    }
//    //将r变成他所在根
//    void mroot(int r)
//    {
//        Access(r);
//        Splay(r);
//        update_rev(r);
//    }
//    //调用后u是原来u和v的lca,v和ch[u][1]分别存折lca的两个儿子
//    void lca(int &u,int &v)
//    {
//        Access(v),v=0;
//        while(u)
//        {
//            Splay(u);
//            if(!pre[u])return;
//            rt[ch[u][1]]=true;
//            rt[ch[u][1]=v]=false;
//            u=pre[v=u];
//        }
//    }
//    //将u合并到v上
//    void link(int u,int v)
//    {
//        mroot(u);
//        pre[u]=v;
//    }
//    //将v和他的父节点分离
//    void cut(int v)
//    {
//        //mroot(v);
//        Access(v);
//        Splay(v);
//        pre[ch[v][0]]=0;
//        pre[v]=0;
//        rt[ch[v][0]]=true;
//        ch[v][0]=0;
//    }
//    void init()
//    {
//        memset(head,-1,sizeof(head));
//        memset(pre,0,sizeof(pre));
//        memset(ch,0,sizeof(ch));
//        memset(rev,0,sizeof(rev));
//        //memset(add,0,sizeof(add));
//        for(int i=0;i<=N;i++)rt[i]=true;
//        //maxv[0]=-INF;
//    }
//}tree;
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=10010;
struct Node
{
    Node *p,*ch[2];
    int rev,val;
}node[maxn],*cur,*null;
Node *NewNode()
{
    cur->p=cur->ch[0]=cur->ch[1]=null;
    cur->rev=0;
    return cur++;
}
void init()
{
    null=node;
    null->p=null->ch[0]=null->ch[1]=null;
    null->rev=0;
    cur=node+1;
}
struct LCT
{
    bool isroot(Node *x)
    {
        return x==null||x->p->ch[0]!=x&&x->p->ch[1]!=x;
    }
    void pushup(Node *x)
    {
        //x->mx=max(x->val,max(x->ch[0]->mx,x->ch[1]->mx));
    }
    void pushdown(Node *x)
    {
        if(x==null)return;
        if(x->rev)
        {
            x->rev=0;
            if(x->ch[0]!=null)x->ch[0]->rev^=1;
            if(x->ch[1]!=null)x->ch[1]->rev^=1;
            swap(x->ch[0],x->ch[1]);
        }

    }
    void rotate(Node *x,int f)
    {
        if(isroot(x)) return;
        Node *y=x->p;
        y->ch[!f]=x->ch[f];
        x->p=y->p;
        if(x->ch[f]!=null) x->ch[f]->p=y;
        if(y!=null)
        {
            if(y==y->p->ch[1]) y->p->ch[1]=x;
            else if(y==y->p->ch[0]) y->p->ch[0]=x;
        }
        x->ch[f]=y;
        y->p=x;
        pushup(y);
    }
    void Splay(Node *x)
    {
        static Node *sta[maxn];
        int top=1;
        sta[0]=x;
        for(Node *y=x;!isroot(y);y=y->p)
            sta[top++]=y->p;
        while (top) pushdown(sta[--top]);
        while (!isroot(x))
        {
            Node *y=x->p;
            if(isroot(y)) rotate(x,x==y->ch[0]);
            else
            {
                int f=y->p->ch[0]==y;
                if(y->ch[f]==x) rotate(x,!f);
                else rotate(y,f);
                rotate(x,f);
            }
        }
        pushup(x);
    }
    Node *access(Node *u)
    {
        Node *v=null;
        while (u!=null)
        {
            Splay(u);
            v->p=u;
            u->ch[1]=v;
            pushup(u);
            v=u;
            u=u->p;
        }
        return v;
    }
    Node *link(Node *u,Node *v)//合并
    {
        access(u);
        Splay(u);
        u->rev=1;
        u->p=v;
    }
    Node *cut(Node *u)//分离
    {
        access(u);
        Splay(u);
        u->ch[0]=u->ch[0]->p=null;
        pushup(u);
    }
    void changeroot(Node *u)
    {
        access(u)->rev^=1;
    }
    Node *getroot(Node *u)//找根
    {
        access(u);
        Splay(u);
        while (u->ch[0]!=null) u=u->ch[0];
        Splay(u);
        return u;
    }
    bool judge(Node *u,Node *v)//判断是否在同一子树
    {
        while (u->p!=null) u=u->p;
        while (v->p!=null) v=v->p;
        return u==v;
    }
}tree;
int N,Q;
int main()
{
    char op[10];
    int u,v;
    while(scanf("%d%d",&N,&Q)!=EOF)
    {
        init();
        for(int i=0;i<=N;i++)NewNode();
        while(Q--)
        {
            scanf("%s%d%d",op,&u,&v);
            if(op[0]=='C')
                tree.link(node+u,node+v);
            else if(op[0]=='D')
            {
                tree.changeroot(node+u);
                tree.access(node+v);
                tree.getroot(node+v);
                tree.cut(node+v);
            }
            else
            {
                bool ans=tree.judge(node+u,node+v);
                printf("%s\n",ans?"Yes":"No");
            }
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值