[BZOJ3510]-首都-LCT维护重心

说在前面

终于知道…为什么自己惧怕写高sha端bi数据结构了
原来me的代码不仅自带大常数,还自带大长度


题目

BZOJ3510传送门

题目大意

有N个独立的节点,你需要维护接下来的M个操作,这M个操作分为如下三种:
1、 A x y :在x和y之间连一条边,保证x和y在该操作前不联通
2、 Q x :询问x所在联通块的重心
3、 Xor :询问所有联通块的重心的异或和

输入输出格式

输入格式:
第一行两个数字N , M,含义如题
接下来M行,每行描述一个操作,输入的格式如题

输出格式

对于每个Q操作和每个Xor操作,你需要输出一行一个整数回答询问


解法

看到这种有连边操作,并且保证是一棵树的,直接上LCT干死它

这道题的难点就是需要维护重心。
A操作很明显是Link操作。如果可以在A操作的时候顺便维护重心,那么询问操作就是O(1)的。
考虑如何维护,如果直接把两棵树连接在一起,显然是不太好维护的(需要让原来的重心在树里不断调整位置,但是LCT上只能访问链信息)。但是如果在一棵树上连接一个节点,重心要么保持不变,要么向新加节点的方向移动一个点(显然),这个是很方便维护的。
于是考虑启发式合并,每次合并两棵树的时候,把siz较小的树的节点逐个加入siz较大的树,并在加入的过程中维护重心。记录一棵树的siz可以用并查集实现。每次link操作之后,都在外面存一条实边(路径),这样在合并的时候就可以直接使用dfs访问一个联通块的所有节点。

关于时间复杂度,因为每次暴力的将较小的树加入较大的树时,siz至少会增加一倍(对于较小的树来说),那么较小树中的节点最多贡献 log(N) 次合并复杂度,而每次合并都是 log(N) 的,因此单个点最多贡献 log(N)2 的合并复杂度,所以总复杂度 Nlog(N)2


下面是自带大常数的代码

/**************************************************************
    Problem: 3510
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:2436 ms
    Memory:7072 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , M , xsum , core[100005] , head[100005] , tp ;
struct Union_set{
    int fa[100005] , siz[100005] ;
    void init(){
        for( int i = 1 ; i <= N ; i ++ )
            fa[i] = i , siz[i] = 1 ;
    }
    int find( int x ){
        if( fa[x] == x ) return x ;
        return fa[x] = find( fa[x] ) ;
    }
}U ;

struct Path{
    int pre , to ;
}p[200005] ;

struct Node{
    bool rev ;
    int rsiz , isiz , id ;
    Node *ch[2] , *fa ;
    void rever(){
        swap( ch[0] , ch[1] ) ;
        rev ^= 1 ;
    }
    void update(){
        rsiz = 1 ;
        if( ch[0] ) rsiz += ch[0]->rsiz + ch[0]->isiz ;
        if( ch[1] ) rsiz += ch[1]->rsiz + ch[1]->isiz ;
    }
    void pushdown(){
        if( rev ){
            if( ch[0] ) ch[0]->rever() ;
            if( ch[1] ) ch[1]->rever() ;
            rev = false ;
        }
    }
} w[100005] ;

void In( int t1 , int t2 ){
    p[++tp].pre = head[t1] ;
    p[ head[t1] = tp ].to = t2 ;
}

bool isroot( Node *x ){
    if( !x->fa ) return true ;
    if( x->fa->ch[0] != x && x->fa->ch[1] != x ) return true ;
    return false ;
}

void Rotate( Node *nd , const int &aim ){
    Node *x = nd->ch[aim] ;
    if( nd->fa ){
        if( nd->fa->ch[0] == nd ) nd->fa->ch[0] = x ;
        if( nd->fa->ch[1] == nd ) nd->fa->ch[1] = x ;
    } x->fa = nd->fa ;

    if( x->ch[aim^1] ){
        x->ch[aim^1]->fa = nd ;
    } nd->ch[aim] = x->ch[aim^1] ;

    x->ch[aim^1] = nd , nd->fa = x ;
    nd->update() ;
    x->update() ;
}

int topp ;
Node *sta[100005] ;
void Splay( Node *nd ){
    Node *tmp = nd ; topp = 0 ;
    for( ; !isroot( tmp ) ; tmp = tmp->fa ) sta[++topp] = tmp ;
    sta[++topp] = tmp ;
    for( ; topp ; topp -- ) sta[topp]->pushdown() ;

    while( !isroot( nd ) ){
        Node *fa = nd->fa , *gdfa = fa->fa ;
        int pn = ( fa->ch[1] == nd ) , pf ;
        if( gdfa && !isroot( fa ) ){
            pf = ( gdfa->ch[1] == fa ) ;
            if( pn == pf ){
                Rotate( gdfa , pf ) ;
                Rotate( fa , pn ) ;
            } else {
                Rotate( fa , pn ) ;
                Rotate( gdfa , pf ) ;
            }
        } else Rotate( fa , pn ) ;
    }
}

void Access( Node *nd ){
    Node *tmp = NULL ;
    while( nd ){
        Splay( nd ) ;
        if( tmp ) nd->isiz -= tmp->rsiz + tmp->isiz ;
        if( nd->ch[1] ) nd->isiz += nd->ch[1]->rsiz + nd->ch[1]->isiz ;
        nd->ch[1] = tmp ;
        nd->update() ;
        tmp = nd ;
        nd = nd->fa ;
    }
}

void Makeroot( Node *x ){
    Access( x ) ; Splay( x ) ;
    x->rever() ;
}

void Link( Node *y , Node *x ){// link y to x 
    Makeroot( y ) ;
    Access( x ) ; Splay( x ) ;
    y->fa = x ; x->isiz += y->rsiz + y->isiz ;

    Node *G = ( w + core[U.find(x->id)] ) ;
    Makeroot( G ) ;
    Access( y ) ; Splay( G ) ;//Splay to update     

    Node *tmp = G->ch[1] ;
    while( tmp->ch[0] ){
        tmp = tmp->ch[0] ;
        tmp->pushdown() ;
    }
    Access( tmp ) ; Splay( G ) ;//Splay to Makeroot G and update G 
    int tmpsiz2 = ( tmp->isiz + 1 ) * 2 , totsiz = G->isiz + G->rsiz ;
    if( tmpsiz2 > totsiz || ( tmpsiz2 == totsiz && tmp->id < G->id ) ){
        core[ U.find(G->id) ] = tmp->id ;
        xsum = ( xsum ^ G->id ^ tmp->id ) ;
    }
}

void dfs( int u , int f ){ //reset u ,then Link u to f
    Node *nd = ( w + u ) ;
    nd->rev = false ;
    nd->rsiz = 1 ; nd->isiz = 0 ;
    nd->fa = nd->ch[0] = nd->ch[1] = NULL ;
    Link( nd , w + f ) ;
    for( int i = head[u] ; i ; i = p[i].pre )
        if( p[i].to != f ) dfs( p[i].to , u ) ;
}

void solve(){
    char opt[6] ;
    for( int i = 1 , a , b ; i <= M ; i ++ ){
        scanf( "%s" , opt ) ;
        if( opt[0] == 'X' )
            printf( "%d\n" , xsum ) ;
        else if( opt[0] == 'Q' ){
            scanf( "%d" , &a ) ;
            printf( "%d\n" , core[ U.find(a) ] ) ;
        } else {
            scanf( "%d%d" , &a , &b ) ;

            int F_a = U.find( a ) , F_b = U.find( b ) ;
            if( U.siz[F_a] < U.siz[F_b] ) // a is bigger
                swap( a , b ) , swap( F_a , F_b ) ;
            xsum ^= core[F_b] ;
            U.siz[F_a] += U.siz[F_b] ;
            U.fa[F_b] = F_a ;

            dfs( b , a ) ;
            In( a , b ) ; In( b , a ) ;
        }
    }
}

void Program_initialize(){
    U.init() ;
    for( int i = 1 ; i <= N ; i ++ ){
        xsum ^= i ;
        core[i] = i ;
        w[i].id = i ; w[i].rev = false ;
        w[i].rsiz = 1 ; w[i].isiz = 0 ;
        w[i].fa = w[i].ch[0] = w[i].ch[1] = NULL ;
    }
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    Program_initialize() ;
    solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值