Splay模板[POJ2761]+[BZOJ3223]+[BZOJ3224]

说在前面

本来昨天想学LCT,其中有一部分要用到Splay
然后me发现自己好像还没有写过Splay,虽然操作都知道,不过感觉不太稳。于是去找了一道题练练手,然而写了很久也过不了。
想到之前写过一道Treap,想借助那个Treap代码来调试WA掉的Splay
结果不仅Splay没调出来,顺便还发现了那份treap代码的bug,黑人问号脸.jpg = =?
真的当时内心绝望…

不过经过长达一天的努力,终于搞定了!while(1) 开心++,成就感++;
花的时间感觉挺多的。不过呢,把之前初学平衡树时候的很多疑问搞清楚了,还成功地写出了自己的板子,也是值得的


BZOJ3223-文艺平衡树

BZOJ3223传送门

题面

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

输入输出格式

输入格式:
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n

输出格式:
输出一行n个数字,表示原始序列经过m次变换后的结果

解法

一道比较基础的Splay题,核心是reverse标记
先由下标建好一个Splay
对于每个操作[L,R],把第L-1个节点Splay到根,这时候第R+1个节点一定在当前根的右子树内
然后再把第R+1个节点Splay到L的下方,那么区间[L,R]就是第R+1个节点的左儿子(极为x)以及它的子树。正确性由平衡树定义得到。
在x节点处打一个reverse标记,然后将左右子树互换,像线段树一样用标记推下去。因为这棵Splay是由下标(位置)建的树,所以交换左右儿子相当于是交换了位置。

注意Splay操作的时候,如果是Splay到根,需要同时更新root

自带大常数的代码

/**************************************************************
    Problem: 3223
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:2224 ms
    Memory:3856 kb
****************************************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define loop(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
struct treap{
    int siz,val;
    bool reser;
    treap* ch[2];
    treap *fa;
    void updata(){
        this->siz=1;
        if(this->ch[0]!=NULL) this->siz+=this->ch[0]->siz;
        if(this->ch[1]!=NULL) this->siz+=this->ch[1]->siz;
    }
    void pushdown(){
        if(this->reser==true){
            swap(this->ch[0],this->ch[1]);
            this->reser=false;
            if(this->ch[0]!=NULL)    this->ch[0]->reser^=1;
            if(this->ch[1]!=NULL)    this->ch[1]->reser^=1;
        }
    }
    treap(treap* k,int x){
        this->val=x;
        this->fa=k;
        this->reser=false;
        this->siz=1;
    }
};
int n,m;
treap *root=NULL;

void dfs( treap *nd ){
    printf( "%d -> %d(%d %d)\n" , nd->fa?nd->fa->val:-1 , nd->val , nd->ch[0]?nd->ch[0]->val:-1 , nd->ch[1]?nd->ch[1]->val:-1 ); ;
    if( nd->ch[0] ) dfs( nd->ch[0] ) ;
    if( nd->ch[1] ) dfs( nd->ch[1] ) ;
}

treap *build(int L,int R,treap *fa){
    if(L>R)  return NULL;
    int mid=(L+R)/2;
    treap *t=new treap(fa,mid);
    t->ch[0]=build(L,mid-1,t);
    t->ch[1]=build(mid+1,R,t);
    t->updata();
    return t;
} 

void Rotate( treap *&nd , int aim ){
    treap *x = nd -> ch[aim] ;

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


inline void Splay( treap *&nd , treap* aim ){
    while( nd -> fa != aim ){
        treap *fa = nd -> fa , *gdfa = nd -> fa -> fa ;
        int pn = ( (nd == fa -> ch[1]) ? 1 : 0 ) , pf ;
        if( gdfa && gdfa != aim ){
            pf = ( fa == gdfa -> ch[1] ) ;
            if( pn == pf ){
                Rotate( gdfa , pn ) ;
                Rotate( fa , pf ) ;
            } else {
                Rotate( fa , pn ) ;
                Rotate( gdfa , pf ) ;
            }
        } else
            Rotate( fa , pn ) ;
        if( nd->fa == aim && aim == NULL ) root = nd ; 
    }
}

inline treap* Kth( treap *nd , int K ){
    nd->pushdown() ;
    int Lsiz = ( nd -> ch[0] ? nd -> ch[0] -> siz : 0 ) ;
    if( K > Lsiz + 1 ) return Kth( nd -> ch[1] , K - Lsiz - 1 ) ;
    if( K > Lsiz && K <= Lsiz + 1 ) return nd ;
    return Kth( nd -> ch[0] , K ) ;
}

void reverse(int L,int R){
    treap *Lt=Kth(root,L),*Rt=Kth(root,R+2);
    Splay(Lt,NULL);
    root=Lt;
    Splay(Rt,Lt);
    if( Rt->ch[0] ) Rt->ch[0]->reser^=1;
}

void print(treap *t){
    t->pushdown();
    if(t->ch[0]!=NULL)   print(t->ch[0]);
    if(t->val!=0&&t->val!=n+1)    printf("%d ",t->val);
    if(t->ch[1]!=NULL)   print(t->ch[1]);
}

int main(){
    int L,R;
    scanf("%d%d",&n,&m);
    root=build(0,n+1,NULL);
    loop(i,1,m){
        scanf("%d%d",&L,&R);
        reverse(L,R);
    }
    print(root);
    return 0;
}

BZOJ3224普通平衡树

BZOJ3224传送门

题面

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

输入输出格式

输入格式:
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

输出格式:
对于操作3,4,5,6每行输出一个数,表示对应答案

解法

这道题就是一个比较全的板子题了,有很多平衡树的基本操作。

注意这个删除操作的处理,当一个点的出现次数多于一次时,直接cnt–即可
而只有一次时,则需要将该点从树中移除掉。me的办法是先把要删除的点(记为x)Splay到根,然后在左子树里找到当前根的前驱(记为y),记录下来之后把x删掉。然后把y点Splay到根,把原来的右子树接到y的右子树上去。(因为y是x的前驱,也就是原左子树的最大值,Splay之后y没有右子树,所以可以直接接过去 )

自带大常数的代码

/**************************************************************
    Problem: 3224
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:516 ms
    Memory:2284 kb
****************************************************************/

#include "cstdio"  
#include "cstring"
#include "algorithm"
#include "cstdlib"
struct Node{
    Node *ch[2],*fa;
    int cnt,val,fix,siz;
    Node(int x){
        this->val=x;
        fix=rand();
        siz=cnt=0;
        fa=ch[0]=ch[1]=NULL;
    }
    int dcmp(int x){
        if(x==val)  return -1;
        return x > val ? 1 : 0;
    }
    void update(){
        siz=this->cnt;
        if(ch[0])   siz+=ch[0]->siz;
        if(ch[1]) siz+=ch[1]->siz;
    }
} *root , *nnode ;
int n,d,x,beh,fro,tmp,ok;


inline void Insert( Node *&nd , int num ) ;
inline void Rotate( Node *&nd , int aim ) ;
inline void Splay( Node *&nd ) ;
inline void Delete( Node* nd , int num ) ;

void dfs( Node *nd ){
    printf( "%d -> %d(%d %d)\n" , nd->fa?nd->fa->val:-1 , nd->val , nd->ch[0]?nd->ch[0]->val:-1 , nd->ch[1]?nd->ch[1]->val:-1 ); ;
    if( nd->ch[0] ) dfs( nd->ch[0] ) ;
    //printf( "%d " , nd->val ) ;
    if( nd->ch[1] ) dfs( nd->ch[1] ) ;
}

void Rotate( Node *&nd , int aim ){
    Node *x = nd -> ch[aim] ;
//  printf( "Rotate : fa %d  aim %d\n" , nd->val , x->val ) ;
    nd -> ch[aim] = x -> ch[aim^1] ;
    if( x -> ch[aim^1] ) x -> ch[aim^1] -> fa = nd ;
    x -> fa = nd -> fa ;
    if( nd->fa ){
        //printf( "Rotate : now %d  arc %d  x %d\n" , nd , nd->fa->ch[0] , x ) ;
        if( nd == nd->fa->ch[0] ) nd->fa->ch[0] = x ;
        else nd->fa->ch[1] = x ;
        //printf( "Rotate : now %d  arc %d  x %d\n" , nd , nd->fa->ch[0] , x ) ;  
    }
    x -> ch[aim^1] = nd ;
    nd -> fa = x ;
    nd -> update() ;
    x -> update() ;
}

inline void Insert( Node *&nd , int num ){
    if( !nd )
        nd = new Node( num ) ;
    int aim = nd -> dcmp( num ) ;
    if( aim == -1 ){
        nd -> cnt ++ , nd -> siz ++ ;
        nnode = nd ;
        return ;
    } else {
        Insert( nd -> ch[aim] , num ) ;
        nd -> ch[aim] -> fa = nd ;
        nd -> update() ;
    }
}

inline void Splay( Node *&nd ){
    while( nd -> fa != NULL ){
        Node *fa = nd -> fa , *gdfa = nd -> fa -> fa ;
        int pn = ( (nd == fa -> ch[1]) ? 1 : 0 ) , pf ;
        if( gdfa ){
            pf = ( fa == gdfa -> ch[1] ) ;
            if( pn == pf ){
                Rotate( gdfa , pn ) ;
                Rotate( fa , pf ) ;
            } else {
                Rotate( fa , pn ) ;
                Rotate( gdfa , pf ) ;
            }
        } else
            Rotate( fa , pn ) ;
    }       
    if( nd->fa == NULL ) root = nd ;
}

inline void Delete( Node* nd , int num ){
    if( !nd ) return ;
    int aim = nd -> dcmp( num ) ;
    if( aim == -1 ){
        if( nd -> cnt != 1 ){
            nd -> cnt -- , nd -> siz -- ;
            return ;
        } else {
            Splay( nd ) ;
            Node *tmp = nd -> ch[0] ;
            if( !tmp ) root = nd -> ch[1] , nd -> ch[1] -> fa = NULL ;
            else{
                while( tmp -> ch[1] ) tmp = tmp -> ch[1] ;
                nd -> ch[0] -> fa = NULL ; Splay( tmp ) ; 
                if( nd -> ch[1] ){
                    tmp -> ch[1] = nd -> ch[1] ;
                    nd -> ch[1] -> fa = tmp ;
                }
                tmp -> update() ;
            }
        }
    } else{
        nd -> siz -- ;
        Delete( nd -> ch[aim] , num ) ;
    }
    if( nd ) nd->update() ; 
}

int Rank(Node* t,int x){
    if( !t ) return 0x3f3f3f3f ;
    int d = t->dcmp(x),delta;
    if(t->ch[0]==NULL)   delta=0;
    else delta=t->ch[0]->siz;
    if(d==-1){
        Splay( t ) ;
        return delta+1 ;
    }
    else if(d==0)   return Rank(t->ch[d],x);
    else            return Rank(t->ch[1],x)+delta+t->cnt;
}

inline int Kth( Node *nd , int K ){
    int Lsiz = ( nd -> ch[0] ? nd -> ch[0] -> siz : 0 ) ;
//  printf( "Kth :nd(%d %d) K(%d)  Lsiz(%d)\n" , nd , nd -> num , K , Lsiz ) ;
    if( K > Lsiz + nd -> cnt ) return Kth( nd -> ch[1] , K - Lsiz - nd -> cnt ) ;
    else if( K > Lsiz && K <= Lsiz + nd -> cnt ){
        Splay( nd ) ;
        return nd -> val ;
    }
    else return Kth( nd -> ch[0] , K ) ;
}

void be(Node *t,int x){
    if(t==NULL) return;
    int d=t->dcmp(x);
    if(d==1||d==-1)     be(t->ch[1],x);
    else if(d==0)       {beh=t->val;be(t->ch[0],x);}
}

void fr(Node *t,int x){
    if(t==NULL) return;
    int d=t->dcmp(x);
    if(d==1)                    {fro=t->val;fr(t->ch[1],x);}
    else if(d==0||d==-1)        {fr(t->ch[0],x);}
}

inline int read()
{
    int data=0,w=1; char ch=0;
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
    return data*w;
}

int main(){
    n=read();
    for(int i=1;i<=n;i++){
        d=read();x=read();beh=0;fro=0;
        switch(d){
            case 1:Insert(root,x); Splay( nnode ) ; break;
            case 2:Delete(root,x);break;
            case 3:printf("%d\n",Rank(root,x));break;
            case 4:printf("%d\n",Kth(root,x));break;
            case 5:fr(root,x);printf("%d\n",fro);break;
            case 6:be(root,x);printf("%d\n",beh);break;

        }
    }
    return 0;
}

POJ2761

POJ2761传送门

题目大意

给定一个序列和一些询问。每次讯问[L,R]中第k大的数字。输入保证[L,R]不包含。

输入输出格式

输入格式:
第一行包含两个整数N,M,表示序列长度和询问数
接下来M行每行三个数L,R,K,含义如题所述

输出格式:
对于每个询问,输出一个数字表示答案

解法

这道题可以直接上主席树在线搞
不过呢,注意到输入保证讯问的区间不会包含,因此可以离线用Splay等平衡树处理
把所有的询问按照L排序,那么R也一定是单增的,由于题目的保证,这是显然的

现在假设我们已经处理了上一个询问,现在需要处理[L,R]的询问,我们把当前询问没有包含的数字从平衡树里delete掉,然后insert平衡树里还没有的数字,剩下的就是常规查询第K大了

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

int N , M , ori[100005] , ans[50005] ;

struct Queries{
    int L , R , K , id ;
    bool operator < ( const Queries &A ) const {
        return L <  A.L ||
              (L == A.L && R <= A.R ) ;
    }
}Q[50005] ;
struct Node{
    int num , cnt , tot ;
    Node *ch[2] , *fa ;
    Node( int num_ ){
        num = num_ ;
        cnt = tot = 0 ;
        ch[0] = ch[1] = fa = 0 ;
    }
    int dcmp( const int &tmp ){
        if( tmp == num ) return -1 ;
        return tmp > num ;
    }
    void update(){
        tot = cnt ;
        if( ch[0] ) tot += ch[0] -> tot ;
        if( ch[1] ) tot += ch[1] -> tot ;
    }
}*root , *nnode ;


inline void Insert( Node *&nd , int num ) ;
inline void Rotate( Node *&nd , int aim ) ;
inline void Splay( Node *&nd ) ;
inline void Delete( Node* nd , int num ) ;
inline int Kth( Node *nd , int K ) ;

inline void Insert( Node *&nd , int num ){
    if( !nd )
        nd = new Node( num ) ;
    int aim = nd -> dcmp( num ) ;
    if( aim == -1 ){
        nd -> cnt ++ , nd -> tot ++ ;
        nnode = nd ;
        return ;
    } else {
        Insert( nd -> ch[aim] , num ) ;
        nd -> ch[aim] -> fa = nd ;
        nd -> update() ;
    }
}

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

inline void Splay( Node *&nd ){
    while( nd -> fa != NULL){
        Node *fa = nd -> fa , *gdfa = nd -> fa -> fa ;
        int pn = ( (nd == fa -> ch[1]) ? 1 : 0 ) , pf ;
        if( gdfa ){
            pf = ( fa == gdfa -> ch[1] ) ;
            if( pn == pf ){
                Rotate( gdfa , pn ) ;
                Rotate( fa , pf ) ;
            } else {
                Rotate( fa , pn ) ;
                Rotate( gdfa , pf ) ;
            }
        } else
            Rotate( fa , pn ) ;
    }
    if( nd->fa == NULL ) root = nd ; 
}

inline void Delete( Node* nd , int num ){
    if( !nd ) return ;
    int aim = nd -> dcmp( num ) ;
    if( aim == -1 ){
        if( nd -> cnt != 1 ){
            nd -> cnt -- , nd -> tot -- ;
            return ;
        } else {
            Splay( nd ) ;
            Node *tmp = nd -> ch[0] ;
            if( !tmp ) root = nd -> ch[1] , nd -> ch[1] -> fa = NULL ;
            else{
                while( tmp -> ch[1] ) tmp = tmp -> ch[1] ;
                nd -> ch[0] -> fa = NULL ; Splay( tmp ) ;
                if( nd -> ch[1] ){
                    tmp -> ch[1] = nd -> ch[1] ;
                    nd -> ch[1] -> fa = tmp ;
                }
                tmp -> update() ;
            }
        }
    } else{
        nd -> tot -- ;
        Delete( nd -> ch[aim] , num ) ;
    }
    if( nd ) nd->update() ; 
}

inline int Kth( Node *nd , int K ){
    int Lsiz = ( nd -> ch[0] ? nd -> ch[0] -> tot : 0 ) ;
    if( K > Lsiz + nd -> cnt ) return Kth( nd -> ch[1] , K - Lsiz - nd -> cnt ) ;
    else if( K > Lsiz && K <= Lsiz + nd -> cnt ){
        Splay( nd ) ;
        return nd -> num ;
    }
    else return Kth( nd -> ch[0] , K ) ;
}

void solve(){
    sort( Q + 1 , Q + M + 1 ) ;
    int lf = 1 , rg = 0 ;
    for( register int i = 1 ; i <= M ; i ++ ){
        int L = Q[i].L , R = Q[i].R ;
        while( rg < R ){
            rg ++ ;
            Insert( root , ori[rg] ) ; Splay( nnode ) ;
        }
        while( lf < L ){
            Delete( root , ori[lf] ) ;
            lf ++ ;
        }
        ans[Q[i].id] = Kth( root , Q[i].K ) ;
    }
    for( register int i = 1 ; i <= M ; i ++ )
        printf( "%d\n" , ans[i] ) ;
}

inline int read_(){
    int rt = 0;
    char ch = getchar() ;
    while( ch < '0' || ch > '9' ) ch = getchar() ;
    while( ch >='0' && ch <='9' ) rt = rt * 10 + ch - '0' , ch = getchar() ;
    return rt;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 ; i <= N ; i ++ )
        ori[i] = read_() ;
    for( int i = 1 ; i <= M ; i ++ ){
        Q[i].L = read_() , Q[i].R = read_() , Q[i].K = read_() ;
        Q[i].id = i ;
    }
    solve() ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值