BZOJ 2049 动态树-模板题

BZOJ 2049:[Sdoi2008]Cave 洞穴勘测

 

辉辉有一台监测仪器可以实时将通道的每一次改变状况在辉辉手边的终端机上显示:如果监测到洞穴u和洞穴v之间出现了一条通道,终端机上会显示一条指令 Connect u v 。如果监测到洞穴u和洞穴v之间的通道被毁,终端机上会显示一条指令 Destroy u v 。经过长期的艰苦卓绝的手工推算,辉辉发现一个奇怪的现象:无论通道怎么改变,任意时刻任意两个洞穴之间至多只有一条路径。因而,辉辉坚信这是由于某种本质规律的支配导致的。因而,辉辉更加夜以继日地坚守在终端机之前,试图通过通道的改变情况来研究这条本质规律。然而,终于有一天,辉辉在堆积成山的演算纸中崩溃了……他把终端机往地面一砸(终端机也足够坚固无法破坏),转而求助于你,说道:“你老兄把这程序写写吧”。辉辉希望能随时通过终端机发出指令 Query u v,向监测仪询问此时洞穴u和洞穴v是否连通。现在你要为他编写程序回答每一次询问。已知在第一条指令显示之前,JSZX洞穴群中没有任何通道存在。

Input

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

Output

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

Sample Input

Sample Output

200    5

Query      123  127

Connect     123  127

Query       123  127

Destroy     127  123

Query           123  127

3       5

Connect     1     2

Connect     3     1

Query       2     3

Destroy     1     3

Query           2   3

No

Yes

No

 

Yes

No

 

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 10010
struct Node {
    Node *ch[2],*fa;
    int rev,value;
    Node(){
        ch[0] = ch[1] = fa = NULL;
        rev = 0;
    }
    Node(int v){
        ch[0] = ch[1] = fa = NULL;
        rev = 0; value = v;
    }
    int lr_ch(Node *(&node)) {
        this -> push_down();
        return ch[1] == node;
    }
    void link_ch(Node *(&node),int d) {
        ch[d] = node;
        if(node!=NULL) node -> fa = this;
    }
    void push_rev(Node *(&node)) {
        swap(node->ch[0],node->ch[1]);
        node->rev ^= 1;
    }
    void push_down() {
        if(rev == 0) return;
        if(ch[0] != NULL) push_rev(ch[0]);
        if(ch[1] != NULL) push_rev(ch[1]);
        rev = 0;
    }
}*root,*nod[N];
inline int is_Root(Node *(&node));

// 一定要注意splay之前 小棵splay树上根node之间的节点下放
void push_Path(Node *(&node)){
    Node *stack[N],*now = node;
    int top = 0;
while(!is_Root(now)){ 
stack[++top] = now;
        now  =  now -> fa;
}    
stack[++top] = now;
    while(top != 0)   stack[top--] -> push_down();
}
//单旋操作,注意与splay模板的区别
void rotate(Node *(&node),int d) {
    Node *ff = node -> fa -> fa;
    Node *f = node -> fa;
    f -> link_ch(node->ch[d],d^1);
    //当ff为"天花板"时,"天花板"是没有孩子的
    if(!is_Root(f))    ff->link_ch(node,ff->lr_ch(f));
    else            node->fa = ff;
    node -> link_ch(f,d);
}
//注意splay的写法,别用函数递归,防止爆栈
void splay(Node *(&node)){
    push_Path(node);
    while(!is_Root(node)){
        node -> fa -> push_down();  node -> push_down();
        if(is_Root(node->fa)){
            rotate(node,node->fa->ch[0]==node);
            return;
        }
        int  b1 = (node->fa->fa->ch[0]==node->fa);
        int  b2 = (node->fa->ch[0]==node);
        if(b1==b2)   rotate(node->fa,b2);
        else         rotate(node,b2);
        rotate(node,b1);
    }
}
//判断node节点是否为一小棵splay树的根
inline int is_Root(Node *(&node)) {
    if(node->fa->ch[0]==node) return 0;
    if(node->fa->ch[1]==node) return 0;
    return 1;
}
//access操作,打通node节点与"天花板"root
void access(Node *(&node)) {
    Node *up = node,*down = NULL;
    while(up != root) {
        splay(up);
        up -> push_down();
        up -> ch[1] = down;
        down = up;  up = up -> fa;
    }
}
//将大森林的树根换成node!
void re_Root(Node *(&node)) {
    access(node);
    splay(node);
    node -> push_rev(node);
}
//要记住: link操作的前提是link后 不能形成环
void link(Node *(&node1),Node *(&node2)) {
    re_Root(node1);
    node1 -> fa = node2;
    access(node1);
}
//要记住: cut操作node1必须与node2有边存在
void cut(Node *(&node1),Node *(&node2)) {
    re_Root(node2);
    access(node1);
    splay(node1);
    node2 -> fa = root;
    node1 -> push_down();
    node1 -> ch[0] = NULL;
}
//判断node1余node2是否在同一棵小splay树中
int judge(Node *node1,Node *node2){
    while(node1->fa!=root) node1 = node1 -> fa;
    while(node2->fa!=root) node2 = node2 -> fa;
    return node1==node2;
}
//初始化所有节点与"天花板"
void init(int n) {
    root = new Node(-1);
    for(int i=1;i<=n;i++) {
        nod[i] = NULL;
        nod[i] = new Node(i);
        nod[i] -> fa = root;
    }
}
//释放节点所占空间;但是注意nod[i]节点没有被初始化
void delet(Node *(&node)){
    if(node==NULL) return;
    delet(node->ch[0]);
    delet(node->ch[1]);
    delete node;  node = NULL;
}
int main() {
    int n,m,x,y;
    char s[25];
    while(~scanf("%d%d",&n,&m)){
        init(n);
        for(int i = 1;i<=m;i++){
            scanf("%s%d%d",s,&x,&y);
            if(s[0]=='C') link(nod[x],nod[y]);
            if(s[0]=='D') cut(nod[x],nod[y]);
            if(s[0]=='Q') printf("%s\n",judge(nod[x],nod[y])?"Yes":"No");
        }
        delet(root);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值