Splay 学习笔记

鲜花
410里一位玩原神的主播说过:
"先学Splay,还能LCT。"
然而我学完了带旋treap和fhq-treap才来学splay

其实看来平衡树先学splay也是不错的选择。

准备

这部分很简单,相信都能看懂吧。

int val[N],son[N][2],fa[N],sum[N],cnt[N],rt,num;
#define ls(k) son[(k)][0]
#define rs(k) son[(k)][1]
inl int newnode(int x){
    val[++num]=x;sum[num]=cnt[num]=1;
    return num;
}
inl void pushup(int k){sum[k]=sum[ls(k)]+cnt[k]+sum[rs(k)];}
inl int getid(int k){return rs(fa[k])==k;}

rotate

和treap转的方式几乎一样
废话都是BST还能咋转
但二者底层逻辑不同
treap旋转是为了把点转到最底下删了或者维护堆性质
但splay是为了把点转到最顶上

三步概括一下:
假设要把b转上去,f为原A的父亲(左旋右旋不用管,这是treap的操作)
image
1.把b点右儿子e接到父亲a左儿子
2.把父亲a接到b右儿子
3.把b接到f

最后记得pushup

inl void rotate(int k){
    int f=fa[k],g=fa[f],id=getid(k),gid=getid(f);
    son[f][id]=son[k][id^1];fa[son[k][id^1]]=f;//1
    son[k][id^1]=f;fa[f]=k;//2
    if(g)son[g][gid]=k;//3
    fa[k]=g;//可能当前换到根,fa也应清0,所以祖父有没有都要赋值
    pushup(f),pushup(k);
}

splay

保证复杂度的重要操作(需要势能分析,但我不会qwq)
如果g、f、x在一条线上 先转f再转x可以让链长减半
然后就没了

inl void splay(int x){
    for(int f;f=fa[x];rotate(x)){
        if(fa[f])rotate(getid(f)^getid(x)?x:f);
    }
    rt=x;
}

delete

对于treap 我们会把点旋转到叶子再删
那么我们可以用类似fhq的操作 删完合并一下

inl void join(int x,int y){
    int p=x;
    while(rs(p))p=rs(p);
    splay(p);
    rs(rt)=y,fa[y]=rt;
    pushup(rt);
}
inl void del(int x){
    find(x);
    if(cnt[rt]>1)return sum[rt]--,cnt[rt]--,void();
    fa[ls(rt)]=fa[rs(rt)]=0;
    if(!ls(rt))return rt=rs(rt),void();
    if(!rs(rt))return rt=ls(rt),void();
    join(ls(rt),rs(rt));
}

其余操作

按正常平衡树写 注意对每个点操作完后splay一下

inl void ins(int x){
    if(!rt)return rt=newnode(x),void();
    int p=rt,f=0;
    while(1){
        if(val[p]==x){
            cnt[p]++;
            break;
        }
        f=p,p=son[p][x>val[p]];
        if(!p){
            p=newnode(x);
            fa[p]=f,son[f][x>val[f]]=p;
            break;
        }
    }
    pushup(p),pushup(f);
    splay(p);
}
posted @ 2023-12-20 14:21  xiang_xiang  阅读(3)  评论(0编辑  收藏  举报