bzoj 2049: [Sdoi2008]Cave 洞穴勘测 (link-cut-tree)

2049: [Sdoi2008]Cave 洞穴勘测

题意:一幅图,进行三个操作,destroy(a,b)断开a,b之间的边。 connect (a,b),连接在a,b之间连一条边,query(a,b),询问a,b是否联通。任何操作都会保证不会出现环,也就是这幅图是森林。
解题思路:lct模板题。碰到了一点小小的困难,就是destroy操作,之前没有学过换根,这次学了一下。cut(a,b),操作是将a换为根,然后access(a)这样确保a跟b不是在一条链上的,然后splay(b)之后,将b的fa设为0。
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
#include <math.h>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <stdlib.h>
#include <time.h>
#define lowbit(x) (x&(-x))
#define ll __int64
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define LS son[0][rt]
#define RS son[1][rt]
#define FA fa[rt]
#define new_edge(a,b,c) edge[tot].t = b , edge[tot].v = c , edge[tot].next = head[a] , head[a] = tot ++
using namespace std;

const int maxn = 111111 ;

int son[2][maxn] , fa[maxn] , is[maxn] ;
int rev[maxn] ;

void reverse ( int rt ) {
    if ( !rt ) return ;
    rev[rt] ^= 1 ;
    swap ( RS , LS ) ;
}

void push_down ( int rt ) {
    if ( rev[rt] ) {
        reverse ( LS ) ;
        reverse ( RS ) ;
        rev[rt] = 0 ;
    }
}

void push_up ( int rt ) {
}

void init ( int rt ) {
    FA = LS = RS = rev[rt] = 0 ;
    is[rt] = 1 ;
}

void rot ( int rt , int c ) {
    int y = fa[rt] , z = fa[y] ;
    son[!c][y] = son[c][rt] ; fa[son[c][rt]] = y ;
    son[c][rt] = y ; fa[y] = rt ;
    fa[rt] = z ;
    if ( is[y] ) is[y] = 0 , is[rt] = 1 ;
    else son[y==son[1][z]][z] = rt ;
    push_up ( y ) ;
}

void down ( int rt ) {
    if ( !is[rt] ) down ( fa[rt] ) ;
    push_down ( rt ) ;
}

void splay ( int rt ) {
    down ( rt ) ;
    while ( !is[rt] ) {
        if ( is[fa[rt]] ) rot ( rt , rt == son[0][fa[rt]] ) ;
        else {
            int y = fa[rt] , z = fa[y] ;
            int c = ( rt == son[0][y] ) , d = ( y == son[0][z] ) ;
            if ( c == d ) rot ( y , c ) , rot ( rt , c ) ;
            else rot ( rt , c ) , rot ( rt , d ) ;
        }
    }
    push_up ( rt ) ;
}

int access ( int rt ) {
    int v = 0 ;
    for ( ; rt ; rt = fa[rt] ) {
        splay ( rt ) ;
        is[RS] = 1 ; is[v] = 0 ;
        RS = v ;
        v = rt ;
        push_up ( rt ) ;
    }
    return v ;
}

void change_root ( int rt ) {
    access ( rt ) ;
    splay ( rt ) ;
    reverse ( rt ) ;
}

int find_root ( int rt ) {
    while ( fa[rt] ) rt = fa[rt] ;
    return rt ;
}

int query ( int a , int b ) {
    return find_root ( a ) == find_root ( b ) ;
}

void cut ( int a , int b ) {
    if ( query ( a , b ) ) {
        change_root ( a ) ;
        access ( a ) ;//确保a,b不在同一条链上
        splay ( b ) ;
        fa[b] = 0 ;
    }
}

void add ( int a , int b ) {
    if ( !query ( a , b ) ) {
        change_root ( b ) ;
        fa[b] = a ;
    }
}

int main() {
    int n , m , i , j , k ;
    char op[11] ;
    scanf ( "%d%d" , &n , &m ) ;
    for ( i = 1 ; i <= n ; i ++ ) init ( i ) ;
    while ( m -- ) {
        scanf ( "%s" , op ) ;
        int a , b ;
        scanf ( "%d%d" , &a , &b ) ;
        if ( op[0] == 'Q' ) {
            k = query ( a , b ) ;
            if ( k ) puts ( "Yes" ) ;
            else puts ( "No" ) ;
        }
        else if ( op[0] == 'D' ) cut ( a , b ) ;
        else add ( a , b ) ;
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值