【bzoj2049】【LCT】洞穴勘测

3 篇文章 0 订阅
  • 题意:维护森林支持动态插入一条边,删除一条边,查询两点连通性
  • 数据范围:100%的数据满足n≤10000, m≤200000
  • 题解:LCT模板题,注意link-cut时提根即可
#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 21000
using namespace std;
struct node{
    node *ch[2],*fa;int num;
    int rev;
    int dir(){return this==fa->ch[1];}
    void cnct(node *p,int d){ch[d]=p;p->fa=this;}
    void down();
}tnull,*null=&tnull,*tree[MAXN];
void node::down(){
 //标记下传
    if(this==null) return;
    if(rev){
        if(ch[0])ch[0]->rev^=1;
        if(ch[1])ch[1]->rev^=1;
        swap(ch[0],ch[1]);
        rev=0;
    }
}
//新建节点
node *getnode(int a){ 
    node *p=new node;
    p->fa=p->ch[0]=p->ch[1]=null;p->rev=0;
    p->num=a;
}
//判断子树的根
bool isroot(node *p){ 
    return p==null||p!=p->fa->ch[0]&&p!=p->fa->ch[1];
}
//旋转
void rotate(node *p){   
    if(isroot(p)) return;
    int f=p->dir();
    node *x=p->fa;
    if(x==x->fa->ch[0])
    //注意这里要判断是不是父节点的孩子
        x->fa->ch[0]=p;
    else if(x==x->fa->ch[1])
        x->fa->ch[1]=p;
    p->fa=x->fa;
    x->cnct(p->ch[!f],f);
    p->cnct(x,!f);
}
//旋转到根
void splay(node *p){
    static node* sta[MAXN];int top=0;
    sta[++top]=p;
    for(node *k=p;!isroot(k);k=k->fa) 
//因为随机询问从根向下pushdown
        sta[++top]=k->fa;
    while(top)
        sta[top--]->down();
    while(!isroot(p)){
        if(isroot(p->fa)) {rotate(p);return;}
        else
            if(p->dir()==p->fa->dir()) rotate(p->fa),rotate(p);
            else rotate(p),rotate(p);
    }
}
node *access(node *p){
 //access形成到根的重路径
    node *q=null;
    while(p!=null){
        splay(p);
        p->cnct(q,1);
        q=p;
        p=p->fa;
    }
    return q;
}
void mtr(node *p){
 //move_to_root操作 reverse相对深度位置即可
    access(p);
    splay(p);
    p->rev=1;
}
void link(node *p,node *q){
 //link记得先move_to_root
    mtr(p);
    p->fa=q;
}
void cut(node *p,node *q){ //cut先把一个提到根再access另一个之后干掉另一个左孩子
    mtr(p);
    access(q);
    splay(q);
    q->ch[0]=q->ch[0]->fa=null;
}
//找子树的根判断连通
node* getroot(node *p){ 
    while(p->fa!=null)
        p=p->fa;
    return p;
}
int n,m;
char c[100];
int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++)
            tree[i]=getnode(i);
        for(int i=1;i<=m;i++){
            int a,b;scanf("%s",c);
            scanf("%d%d",&a,&b);
            if(c[0]=='Q')
                if(getroot(tree[a])!=getroot(tree[b]))
                    puts("No");
                else
                    puts("Yes");
            else if(c[0]=='C')
                link(tree[a],tree[b]);
            else 
                cut(tree[a],tree[b]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值