bzoj3224 splay板子

开始学习新知识:splay——tree

是个板子题,学习splay可以看博客

https://blog.csdn.net/Clove_unique/article/details/50630280

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAXN 1000000
int ch[MAXN][2],f[MAXN],size[MAXN],cnt[MAXN],key[MAXN];
int sz,root;
inline void clear(int x){
    ch[x][0]=ch[x][1]=f[x]=size[x]=cnt[x]=key[x]=0;
}
inline bool get(int x){
    return ch[f[x]][1]==x;
}
inline void update(int x){//更新结点x的size
    if(x){
        size[x]=cnt[x];
        if(ch[x][0]) size[x]+=size[ch[x][0]];
        if(ch[x][1]) size[x]+=size[ch[x][1]];
    }
}
inline void rotate(int x){//一次旋转
    //得到x的父亲,爷爷,是不是左子树
    int old=f[x],oldf=f[old],whichx=get(x);
    ch[old][whichx]=ch[x][whichx^1];
    f[ch[old][whichx]]=old;
    
    ch[x][whichx^1]=old;f[old]=x;

    f[x]=oldf;
    if(oldf) ch[oldf][ch[oldf][1]==old]=x;

    update(old);update(x);//不要忘记更新size
}
inline void splay(int x){
    for(int fa;fa=f[x];rotate(x))//再把x翻上来
        if(f[fa])//如果fa非根,且x和fa是同一侧,那么先翻转fa,否则先翻转x 
            rotate((get(x)==get(fa))?fa:x);

    root=x;//最后把x设为root    
}
inline void insert(int x){
    if(root==0){//插到空树里
        sz++;ch[sz][0]=ch[sz][1]=f[sz]=0;
        root=sz;size[sz]=cnt[sz]=1;key[sz]=x;
        return;
    }
    int now=root,fa=0;
    while(1){//不断往下寻找直到找到对应值
        if(x==key[now]){
            cnt[now]++;update(now);update(fa);
            splay(now);break;//把now置顶
        }
        fa=now;
        now=ch[now][key[now]<x];//往下搜索x
        if(now==0){//新建结点
            sz++;ch[sz][0]=ch[sz][1]=0;
            f[sz]=fa;
            size[sz]=cnt[sz]=1;
            ch[fa][key[fa]<x]=sz;
            key[sz]=x;
            update(fa);
            splay(sz);//把sz置顶
            break;
        }
    }
}
inline int find(int x){//寻找x所在位置(排名)
    int now=root,ans=0;
    while(1){
        if(x<key[now])//往左子树搜索
            now=ch[now][0];
        else {
            ans+=(ch[now][0]?size[ch[now][0]]:0);
            //找到对应的键值,置顶now,返回
            if(x==key[now]){splay(now);return ans+1;} 
            ans+=cnt[now];
            now=ch[now][1];//往右子树
        }    
    }
}
inline int findx(int x){//找第x名的值
    int now=root;
    while(1){
        if(ch[now][0] && x<=size[ch[now][0]])
            now=ch[now][0];//往左子树搜索
        else {
            int temp=(ch[now][0]?size[ch[now][0]]:0)+cnt[now];//
            if(x<=temp) 
                return key[now];
            x-=temp;now=ch[now][1];//往右子树搜索
        }    
    }        
}        
inline int pre(){//前驱即左子树里的最大值
    int now=ch[root][0];
    while(ch[now][1]) now=ch[now][1];
    return now;
}
inline int next(){//后继是右子树里的最小值
    int now=ch[root][1];
    while(ch[now][0]) now=ch[now][0];
    return now;
}
inline void del(int x){
    int whatever=find(x);//只是把x置顶
    //x的个数>1
    if(cnt[root]>1){cnt[root]--;update(root);return;}
    //单个x
    if(!ch[root][0] && !ch[root][1]){clear(root);root=0;return;}
    //只有左子树或者只有右子树
    if(!ch[root][0]){//删掉根,右儿子做根
        int oldroot=root;root=ch[root][1];f[root]=0;clear(oldroot);return;
    }
    if(!ch[root][1]){
        int oldroot=root;root=ch[root][0];f[root]=0;clear(oldroot);return;
    }
    //前驱作为根(前驱是没有右儿子的),右子树挂到前驱的右子树,删掉根
    else {
        int pree=pre(),oldroot=root;
        splay(pree);
        ch[root][1]=ch[oldroot][1];
        f[ch[oldroot][1]]=pree;
        clear(oldroot);
        update(pree);
    }
}
int main(){
    int n,op,x;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&op,&x);
        switch(op){
            case 1:insert(x);break;
            case 2:del(x);break;
            case 3:printf("%d\n",find(x));break;
            case 4:printf("%d\n",findx(x));break;
            case 5:insert(x);printf("%d\n",key[pre()]);del(x);break;
            case 6:insert(x);printf("%d\n",key[next()]);del(x);break;
        }
    }
}

 

posted on 2018-11-19 12:44  zsben  阅读(183)  评论(0编辑  收藏  举报

导航