【伸展树splay学习小记】

本文以【bzoj】【1588】为例

基本操作

splay是基本操作,rotate是splay的基本操作。splay(now,root)把now splay到root的下面。单旋和双旋就不说了。我们可以简化操作,如果是一字型(方向相同)则先旋father,否则先旋now。然后再旋now。

一字型

原图
rotate father后
rotate now后

之字型

原图
rotate now后
rotate now后

故可合并成

void splay(int now,int root){
    while(father[now]!=root){
        if(father[father[now]]!=root){
            if(flag(now)==flag(father[now]))rotate(father[now]);
            else rotate(now);
        }
        rotate(now);
    }
}
rotate now是指将now绕father rotate。有两个方向,表示now是father的左儿子还是右儿子。
bool flag(int now){
    return son[father[now]][1]==now;
}
通过方向标记,我们可以将两个方向的rotate合成一个。
void rotate(int now){
    int fa=father[now],fx=flag(now);
    son[fa][fx]=son[now][!fx];
    if(son[now][!fx])father[son[now][!fx]]=fa;
    //连接father和son[now]
    son[father[fa]][flag(fa)]=now;
    father[now]=father[fa];
    //连接grandpa和now
    son[now][!fx]=fa;
    father[fa]=now;
    //连接now和father
    updata(fa);updata(now);
}
具体步骤请自行脑补。
别忘了rotate完成后要updata。
void updata(int pos){
    size[pos]=size[son[pos][0]]+size[son[pos][1]]+1;
}

寻找k小值的标号

int kth(int now,int th){
    if(size[son[now][0]]+1==th)return now;
    else if(size[son[now][0]]+1>th)return kth(son[now][0],th);
    else return kth(son[now][1],th-size[son[now][0]]-1);
}

寻找前驱

int pre(int val){
    int tmp=kth(son[val][0],size[son[val][0]]);
    splay(tmp,val);
    return key[tmp];
}

寻找后继

int succ(int val){
    int tmp=kth(son[val][1],1);
    splay(tmp,val);
    return key[tmp];
}

插入

首先要找到一个合适的位置
int getpos(int now,int val){
    if((key[now]<val)&&(size[son[now][1]]))return getpos(son[now][1],val);
    else if((key[now]>val)&&(size[son[now][0]]))return getpos(son[now][0],val);
    return now;
}
但是我们并不能保证这个点是大于还是小于插入节点
void insert(int root,int pos,int val){
    int sroot=father[root];
    splay(root=getpos(root,val),sroot);
    if(key[root]<val){
        succ(root);//十分重要
        rotate(root=son[root][1]);
    }
    int tmp=son[root][0];key[pos]=val;
    son[root][0]=pos;father[pos]=root;
    son[pos][0]=tmp;father[tmp]=pos;
    updata(pos);updata(root);
    splay(pos,0);
}
我们把刚好比插入点大的点作为根,在左子树插入。如果根小于插入点,则要将根的后继变成根(!!!注意不是根的右儿子!!!)
至此splay的基本操作我们都完成了。

更新:

ps

对于有重复元素的题,我们必须小心小心再小心。find的时候必须找最小序号的哪一个,pre和succ查询的数可能不在splay中,要做一点处理。

删除

void dele(int root,int val){
    int sroot=father[root];
    splay(root=find(root,val),sroot);
    if(son[root][1])splay(succ(root),root);
    son[sroot][flag(root)]=son[root][1];
    father[son[root][1]]=sroot;
    son[son[root][1]][0]=son[root][0];
    father[son[root][0]]=son[root][1];
    updata(son[root][1]);
}

查找标号

int rank(int now,int val){
    if(key[now]==val)return size[son[now][0]]+1;
    else if(val<key[now])return rank(son[now][0],val);
    else if(val>key[now])return size[son[now][0]]+1+rank(son[now][1],val);
}

另外一个查找

int find(int now,int val){//pos
    if(mx[son[now][0]]>=val)return find(son[now][0],val);
    if(key[now]==val)return now;
    if(mi[son[now][1]]<=val)return find(son[now][1],val);
    return now;
}

【bzoj】【1588】【code】

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=1000000,inf=2147483647;
int n,size[maxn+10],key[maxn+10],father[maxn+10],son[maxn+10][10];
void updata(int pos){
    size[pos]=size[son[pos][0]]+size[son[pos][1]]+1;
}
bool flag(int now){
    return son[father[now]][1]==now;
}
void rotate(int now){
    int fa=father[now],fx=flag(now);
    son[fa][fx]=son[now][!fx];
    if(son[now][!fx])father[son[now][!fx]]=fa;
    son[father[fa]][flag(fa)]=now;
    father[now]=father[fa];
    son[now][!fx]=fa;
    father[fa]=now;
    updata(fa);updata(now);
}
void splay(int now,int root){
    while(father[now]!=root){
        if(father[father[now]]!=root){
            if(flag(now)==flag(father[now]))rotate(father[now]);
            else rotate(now);
        }
        rotate(now);
    }
}
int getpos(int now,int val){
    if((key[now]<val)&&(size[son[now][1]]))return getpos(son[now][1],val);
    else if((key[now]>val)&&(size[son[now][0]]))return getpos(son[now][0],val);
    return now;
}
int kth(int now,int th){
    if(size[son[now][0]]+1==th)return now;
    else if(size[son[now][0]]+1>th)return kth(son[now][0],th);
    else return kth(son[now][1],th-size[son[now][0]]-1);
}
int pre(int val){
    int tmp=kth(son[val][0],size[son[val][0]]);
    splay(tmp,val);
    return key[tmp];
}
int succ(int val){
    int tmp=kth(son[val][1],1);
    splay(tmp,val);
    return key[tmp];
}
void insert(int root,int pos,int val){
    int sroot=father[root];
    splay(root=getpos(root,val),sroot);
    if(key[root]<val){
        succ(root);
        rotate(root=son[root][1]);
    }
    int tmp=son[root][0];key[pos]=val;
    son[root][0]=pos;father[pos]=root;
    son[pos][0]=tmp;father[tmp]=pos;
    updata(pos);updata(root);
    splay(pos,0);
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d",&n);
    key[n+1]=-inf/2;key[n+2]=inf/2;
    size[n+1]=size[n+2]=1;
    son[0][1]=n+2;father[n+2]=0;
    son[n+2][0]=n+1;father[n+1]=n+2;
    int ans=0;
    fo(i,1,n){
        int x;scanf("%d",&x);
        insert(son[0][1],i,x);
        if(i==1)ans+=x;
        else ans+=min(x-pre(i),succ(i)-x);
        printf("%d\n",ans);
    }
    printf("%d\n",ans);
    return 0;
}

【bzoj】【3224】【Tyvj】【1728】【普通平衡树】

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=100000,inf=2147483647;
int n,key[maxn+10],mx[maxn+10],mi[maxn+10],size[maxn+10],father[maxn+10],son[maxn+10][10];
bool flag(int now){
    return son[father[now]][1]==now;
}
void updata(int now){
    size[now]=size[son[now][0]]+size[son[now][1]]+1;
    mx[now]=max(key[now],max(mx[son[now][0]],mx[son[now][1]]));
    mi[now]=min(key[now],min(mi[son[now][0]],mi[son[now][1]]));
}
void rotate(int now){
    int fa=father[now],fx=flag(now);
    son[fa][fx]=son[now][!fx];
    if(son[now][!fx])father[son[now][!fx]]=fa;
    son[father[fa]][flag(fa)]=now;
    father[now]=father[fa];
    son[now][!fx]=fa;
    father[fa]=now;
    updata(fa);updata(now);
}
void splay(int now,int root){
    while(father[now]!=root){
        if(father[father[now]]!=root)
            if(flag(now)==flag(father[now]))rotate(father[now]);else rotate(now);
        rotate(now);
    }
}
int find(int now,int val){//pos
    if(mx[son[now][0]]>=val)return find(son[now][0],val);
    if(key[now]==val)return now;
    if(mi[son[now][1]]<=val)return find(son[now][1],val);
    return now;
}
int kth(int now,int val){//pos
    if(size[son[now][0]]+1==val)return now;
    else if(val<size[son[now][0]]+1)return kth(son[now][0],val);
    else return kth(son[now][1],val-size[son[now][0]]-1);
}
int succ(int now){//pos
    int tmp=kth(son[now][1],1);
    splay(tmp,now);
    return tmp;
}
void insert(int root,int pos,int val){
    int sroot=father[root];
    splay(root=find(root,val),sroot);
    if(key[root]<val){
        splay(succ(root),root);
        rotate(root=son[root][1]);
    }
    key[pos]=val;int tmp=son[root][0];
    son[root][0]=pos;father[pos]=root;
    son[pos][0]=tmp;father[tmp]=pos;
    updata(pos);updata(root);
}
int pre(int now){//pos
    int tmp=kth(son[now][0],size[son[now][0]]);
    splay(tmp,now);
    return tmp;
}
void dele(int root,int val){
    int sroot=father[root];
    splay(root=find(root,val),sroot);
    if(son[root][1])splay(succ(root),root);
    son[sroot][flag(root)]=son[root][1];
    father[son[root][1]]=sroot;
    son[son[root][1]][0]=son[root][0];
    father[son[root][0]]=son[root][1];
    updata(son[root][1]);
}
int rank(int now,int val){
    if(key[now]==val)return size[son[now][0]]+1;
    else if(val<key[now])return rank(son[now][0],val);
    else if(val>key[now])return size[son[now][0]]+1+rank(son[now][1],val);
}
int main(){
    //freopen("d.in","r",stdin);
    //freopen("d.out","w",stdout);
    freopen("input9.in","r",stdin);
    freopen("input9+.out","w",stdout);
    scanf("%d",&n);
    mx[0]=-inf/2;mi[0]=inf/2;
    size[n+1]=size[n+2]=1;
    key[n+1]=-inf/2;key[n+2]=inf/2;
    son[0][1]=n+2;father[n+2]=0;
    son[n+2][0]=n+1;father[n+1]=n+2;
    fo(i,1,n){
        int opt,x;scanf("%d%d",&opt,&x);
        if(opt==1)insert(son[0][1],i,x);
        else if(opt==2)dele(son[0][1],x);
        else if(opt==3){
            int tmp=find(son[0][1],x);splay(tmp,0);
            printf("%d\n",size[son[tmp][0]]);
        }
        else if(opt==4){
            int tmp=kth(son[0][1],x+1);splay(tmp,0);
            printf("%d\n",key[tmp]);
        }
        else if(opt==5){
            int tmp=find(son[0][1],x);splay(tmp,0);
            if(key[tmp]>=x)printf("%d\n",key[pre(tmp)]);
            else printf("%d\n",key[tmp]);
        }
        else{
            int tmp=find(son[0][1],x+1);splay(tmp,0);
            if(key[tmp]<=x)printf("%d\n",key[succ(tmp)]);
            else printf("%d\n",key[tmp]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值