[BZOJ3159]-决战-LCT+Splay

5 篇文章 0 订阅

说在前面

昨天晚饭后和zyc在操场散步。
me说me今晚要A了决战那个水题,顺便再去写一两道数论水题
zyc表示me想多了,说me肯定会写一晚上决战
然而,前一天晚上只用了一个半小时就1A了首都的me,当然是一脸不服

然后?然后昨晚me果然没有切掉那道题…
一开始样例RE,静态查错找了一堆错出来
后来去看了别人的代码,又找了一些错误出来…然而还是没有过样例…直到昨晚离开机房仍然样例RE

到现在才发现me读入错了= =?改了就A了,内心绝望…
干死zyc!!! @Maxmercer


题目

BZOJ3159传送门
不是权限题,就不复制题面了


解法

这道题貌似民间解法有两个,LCT和树剖(官解貌似是LCT来着)

链加,链上询问最大最小和,LCT都可以轻松维护(把链提取出来,要询问的询问,修改就打标记)
关键是那个链权值反转操作。权值反转很容易想到要用Splay维护,但是并不能直接使用LCT里的那个Splay。因为LCT的Splay维护的是形态信息,如果直接在LCT的Splay里打翻转标记,不仅达不到翻转数值的效果,反而还把形态(深度)信息破坏了。

所以咱们再开一个Splay森林来维护权值,每棵形态Splay都对应一个权值Splay,在维护的时候只需要把形态Splay和权值Splay同步即可。链加,链询问最大最小和操作都在权值Splay上进行。Makeroot的翻转操作需要将两个Splay都翻转,而题目里的翻转操作只翻转权值Splay。这样就可以完美的维护所有的信息了

具体实现:

在形态Splay里存下对应的权值Splay的根(方便操作)。形态Splay里的fa指针不仅是Splay的信息,同时也是原树上的fa&&child信息。而权值Splay里的fa指针仅仅是Splay的信息,因此这里在Access的时候要特别注意,细节特别多,一定要想好再写。
然后就是,形态Splay和权值Splay可能并不长得一样,但是形态Splay里的第k个节点对应着Splay里的第k个节点,因此Access的时候需要用一个Find_Kth去找到对应节点。
还有,形态Splay里所有节点都要存下对应的权值Splay的根(也是为了方便写和维护),因此除了reverse标记,还应该有一个Node_flag标记。

其他细节应该还有不少,但是主要的大概就是这些了,这题很能练练码力的
如果你准备切掉这道题,或者正在debug,加油!!!


下面是自带大常数的代码

/**************************************************************
    Problem: 3159
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:3212 ms
    Memory:6096 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
 
int N , M ;
struct Node{
    bool isv , rev ;
    long long sum ;
    int minn , maxn , now , siz , addflag ;
    Node *ch[2] , *fa , *valrt , *rtflag ;
    void setrt( Node *nd ){
        rtflag = valrt = nd ;
    }
    void rever(){
        swap( ch[0] , ch[1] ) ;
        rev ^= 1 ;
    }
    void add( const int &delta ){
        minn += delta , maxn += delta ;
        sum += 1LL * siz * delta , now += delta ;
        addflag += delta ;
    }
    void update(){ 
        siz = 1 ;
        if( ch[0] ) siz += ch[0]->siz ;
        if( ch[1] ) siz += ch[1]->siz ;
        if( isv ){
            minn = maxn = sum = now ;
            if( ch[0] ){
                minn = min( minn , ch[0]->minn ) ;
                maxn = max( maxn , ch[0]->maxn ) ;
                sum += ch[0]->sum ;
            }
            if( ch[1] ){
                minn = min( minn , ch[1]->minn ) ;
                maxn = max( maxn , ch[1]->maxn ) ;
                sum += ch[1]->sum ;
            }
        }
    }
    void pushdown(){
        if( rev ){
            if( ch[0] ) ch[0]->rever() ;
            if( ch[1] ) ch[1]->rever() ;
            rev = false ;
        }
        if( addflag ){
            if( ch[0] ) ch[0]->add( addflag ) ;
            if( ch[1] ) ch[1]->add( addflag ) ;
            addflag = 0 ;
        }
        if( rtflag ){
            if( ch[0] ) ch[0]->setrt( rtflag ) ;
            if( ch[1] ) ch[1]->setrt( rtflag ) ;
            rtflag = NULL ;
        }
    }
}w [50005] , val[50005] ;
 
void init(){
    for( int i = 1 ; i <= N ; i ++ ){
        val[i].isv = true ;
        w[i].valrt = &val[i] ;
        w[i].siz = val[i].siz = 1 ;
    }
}
 
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 , bool aim ){
    Node *x = nd->ch[aim] ;
    if( nd->fa ){
        if( nd->fa->ch[0] == nd ) nd->fa->ch[0] = x ;
        else 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[50005] ;
void Splay( Node *nd ){
    Node *tmp = nd ;
    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 ;
        bool 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 ) ;
    }
}
 
Node *Kth( Node *x , int K ){
    x->pushdown() ;
    int Lsiz = ( x->ch[0] ? x->ch[0]->siz : 0 ) + 1 ;
    if( K == Lsiz ) return x ;
    if( K < Lsiz ) return Kth( x->ch[0] , K ) ;
    else return Kth( x->ch[1] , K - Lsiz ) ;
}
 
void Access( Node *nd ){
    Node *tmp = NULL , *valnd ;
    while( nd ){
        Splay( nd ) ; Splay( nd->valrt ) ;
        int rk = ( nd->ch[0] ? nd->ch[0]->siz : 0 ) + 1 ;
        valnd = Kth( nd->valrt , rk ) ;//提取值树节点
        Splay( valnd ) ;
        if( valnd->ch[1] ){// Split Right child
            valnd->ch[1]->fa = NULL ;
            nd->ch[1]->setrt( valnd->ch[1] ) ;
        }
        if( tmp ){//Merge Right child
            tmp->valrt->fa = valnd ;
            valnd->ch[1] = tmp->valrt ;
        } else valnd->ch[1] = NULL ;
        valnd->update() ;
 
        nd->ch[1] = tmp ;
        nd->setrt( valnd ) ;//保证位置树的root始终映射到值域Splay的根节点
        nd->update() ;
        tmp = nd ;
        nd = nd->fa ;
    }
}
 
void Makeroot( Node *x ){
    Access( x ) ;//只Access位置树 (实际上也只能Access位置树)
    Splay( x ) ; Splay( x->valrt ) ;
    x->rever() ; x->valrt->rever() ;
}
 
void Link( Node *x , Node *y ){
    Makeroot( x ) ;
    x->fa = y ;
}
 
void solve(){
    char opt[15] ;
    for( int i = 1 , xid , yid , ww ; i <= M ; i ++ ){
        scanf( "%s%d%d" , opt , &xid , &yid ) ;
        Node *x = ( w + xid ) , *y = ( w + yid ) ;
        Makeroot( y ) ; Access( x ) ;
        Splay( x ) ; Splay( x->valrt ) ;// Update Links
        if( opt[0] == 'I' ){
            if( opt[2] == 'c' ){// Increase 
                scanf( "%d" , &ww ) ;
                x->valrt->add( ww ) ;
            } else
                x->valrt->rever() ;// Invert
        } else if( opt[0] == 'M' ){
            if( opt[1] == 'i' ) // Min
                printf( "%d\n" , x->valrt->minn ) ;
            else                // Max
                printf( "%d\n" , x->valrt->maxn ) ;
        } else                  // Sum
            printf( "%lld\n" , x->valrt->sum ) ;
    }
}
 
int main(){
    scanf( "%d%d%*d" , &N , &M ) ;
    init() ;
    for( int i = 1 , u , v ; i < N ; i ++ ){
        scanf( "%d%d" , &u , &v ) ;
        Link( w + u , w + v ) ;
    }
    solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值