splay

\(splay\)

基本操作

简述

Splay通过splay操作(将子节点旋转至根),来维护树高

由于要旋到根,所以要维护节点的父亲,如下

struct Splay{
    int fa,sz,ct,vl,s[2];
}tr[N];
il void clear(cs int x){
    tr[x].vl=tr[x].sz=tr[x].ct=0;
    tr[x].s[0]=tr[x].s[1]=tr[x].fa=0;
}
il bool son(cs int x){return x==tr[tr[x].fa].s[1];}
il void updsz(cs int x){
    tr[x].sz=tr[x].ct+tr[tr[x].s[0]].sz+tr[tr[x].s[1]].sz;
}

\(rotate\)

\(rotate\)操作和\(treap\)的差不多

il void rotate(cs int x){
    int f=tr[x].fa,ff=tr[f].fa;
    if(!f) return;bool l=son(x);//l就是旋转方向
    tr[f].s[l]=tr[x].s[l^1];
    if(tr[x].s[l^1]) tr[tr[x].s[l^1]].fa=f;
    tr[x].s[l^1]=f;//不要忘记更新子节点它爹
    if(ff) tr[ff].s[son(f)]=x;
    tr[x].fa=ff,tr[f].fa=x,updsz(f),updsz(x);
}

\(splay\)

\(splay\)操作可以将节点\(x\)旋转到指定位置(这里是根)

为维护树高,需要双旋(有左旋也有右旋)

具体来说

如果\(x\)和它的父节点同为左儿子或右儿子(共线),先旋转父节点,再旋转\(x\)

如果\(x\)和它的父节点分别为左、右儿子(不共线),直接旋转\(x\)即可

在链很长的时候,一次\(splay\)操作之后,这条链的深度会减半。(可以自己画图看看)

il void splay(cs int x){
    for(ri int f;f=tr[x].fa;rotate(x)){
        if(tr[f].fa) rotate(son(f)^son(x)?x:f);
    }
    rt=x;
}

基本上每次操作之后都要\(splay\)以维护树高 (所以常数是真的大)

\(insert\)

to be continue~

il void insert(cs int x){
    ri int t=rt,f=0;
    while(t&&tr[t].vl!=x) f=t,t=tr[f].s[x>tr[f].vl];
    if(t) tr[t].ct++,splay(t);
    else{
        t=++tot,tr[t].vl=x,tr[t].ct++,tr[t].fa=f;
        tr[f].s[x>tr[f].vl]=t,splay(t);
    }
}

查询数\(x\)的排名

to be continue~

il int rnk(cs int x){
    ri int t=rt,f=0,rk=1;
    while(t){
        if(tr[t].vl>x) t=tr[t].s[0];
        else{
            rk+=tr[tr[t].s[0]].sz;
            if(tr[t].vl==x) break;
            rk+=tr[t].ct,t=tr[t].s[1];
        }
    }
    if(t) splay(t);
    return rk;
}

查询排名为\(x\)的数

to be continue~

il int xth(int x){
    ri int t=rt;
    while(t){
        if(tr[tr[t].s[0]].sz>=x) t=tr[t].s[0];
        else{
            x-=tr[tr[t].s[0]].sz+tr[t].ct;
            if(x<=0) break;t=tr[t].s[1];
        }
    }
    if(t) splay(t);
    return tr[t].vl;
}

查询数\(x\)的前驱&后继

to be continue~

前驱

il int per(){
    int t=tr[rt].s[0];
    if(!t) return 0;
    while(tr[t].s[1]) t=tr[t].s[1];
    return splay(t),t;
}
il int get_per(cs int x){
    insert(x);int k=per();
    del(x);return tr[k].vl;
}

后继

il int nxt(){
    int t=tr[rt].s[1];
    if(!t) return 0;
    while(tr[t].s[0]) t=tr[t].s[0];
    return splay(t),t;
}
il int get_nxt(cs int x){
    insert(x);int k=nxt();
    del(x);return tr[k].vl;
}

发现它们长得很像,可以写一起 (绝对不是偷懒)

il int near(cs bool l){
    int t=tr[rt].s[l^1];
    if(!t) return 0;
    while(tr[t].s[l]) t=tr[t].s[l];
    return splay(t),t;
}
il int sol(cs int x,cs bool l){
    insert(x);int k=near(l);
    del(x);return tr[k].vl;
}

\(ps:delete\)操作见下)

\(delete\)

to be continue~

il void del(cs int x){
    rnk(x);int t=rt;
    if(tr[rt].vl!=x) return;
    if(tr[t].ct>1) tr[t].ct--,updsz(t);
    else if(!tr[t].s[0]&&!tr[t].s[1]) clear(t),rt=0;
    else if(!tr[t].s[0]) rt=tr[t].s[1],clear(t),tr[rt].fa=0; 
    else if(!tr[t].s[1]) rt=tr[t].s[0],clear(t),tr[rt].fa=0;
    else{
        near(1);tr[rt].fa=0,tr[rt].s[1]=tr[t].s[1];
        tr[tr[t].s[1]].fa=rt,clear(t),updsz(rt);
    }
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
namespace _314159_x{
    using namespace std;
    il int rd(){
        ri int x=0,f=1;ri char c=getchar();
        while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
        return x*f;
    }
    short tl=0,wr[20];
    il void wt(int x){
        if(x<0) x=-x,putchar('-');
        do wr[++tl]=x%10,x/=10; while(x);
        while(tl) putchar(wr[tl--]+48);
        putchar('\n'),tl=0;
    }
    cs int N=2e5+5;
    int rt,tot;
    struct Splay{
        int fa,sz,ct,vl,s[2];
    }tr[N];
    il void clear(cs int x){
        tr[x].vl=tr[x].sz=tr[x].ct=0;
        tr[x].s[0]=tr[x].s[1]=tr[x].fa=0;
    }
    il bool son(cs int x){return x==tr[tr[x].fa].s[1];}
    il void updsz(cs int x){
        tr[x].sz=tr[x].ct+tr[tr[x].s[0]].sz+tr[tr[x].s[1]].sz;
    }
    il void rotate(cs int x){
        int f=tr[x].fa,ff=tr[f].fa;
        if(!f) return;bool l=son(x);
        tr[f].s[l]=tr[x].s[l^1];
        if(tr[x].s[l^1]) tr[tr[x].s[l^1]].fa=f;
        tr[x].s[l^1]=f;//!!!!!!!!!!
        if(ff) tr[ff].s[son(f)]=x;
        tr[x].fa=ff,tr[f].fa=x,updsz(f),updsz(x);
    }
    il void splay(cs int x){
        for(ri int f;f=tr[x].fa;rotate(x)){
            if(tr[f].fa) rotate(son(f)^son(x)?x:f);
        }
        rt=x;
    }
    il void insert(cs int x){
        ri int t=rt,f=0;
        while(t&&tr[t].vl!=x) f=t,t=tr[f].s[x>tr[f].vl];
        if(t) tr[t].ct++,splay(t);
        else{
            t=++tot,tr[t].vl=x,tr[t].ct++,tr[t].fa=f;
            tr[f].s[x>tr[f].vl]=t,splay(t);
        }
    }
    il int rnk(cs int x){
        ri int t=rt,f=0,rk=1;
        while(t){
            if(tr[t].vl>x) t=tr[t].s[0];
            else{
                rk+=tr[tr[t].s[0]].sz;
                if(tr[t].vl==x) break;
                rk+=tr[t].ct,t=tr[t].s[1];
            }
        }
        if(t) splay(t);
        return rk;
    }
    il int xth(int x){
        ri int t=rt;
        while(t){
            if(tr[tr[t].s[0]].sz>=x) t=tr[t].s[0];
            else{
                x-=tr[tr[t].s[0]].sz+tr[t].ct;
                if(x<=0) break;t=tr[t].s[1];
            }
        }
        if(t) splay(t);
        return tr[t].vl;
    }
    il int near(cs bool l){
        int t=tr[rt].s[l^1];
        if(!t) return 0;
        while(tr[t].s[l]) t=tr[t].s[l];
        return splay(t),t;
    }
    il void del(cs int x){
        rnk(x);int t=rt;
        if(tr[rt].vl!=x) return;
        if(tr[t].ct>1) tr[t].ct--,updsz(t);
        else if(!tr[t].s[0]&&!tr[t].s[1]) clear(t),rt=0;
        else if(!tr[t].s[0]) rt=tr[t].s[1],clear(t),tr[rt].fa=0; 
        else if(!tr[t].s[1]) rt=tr[t].s[0],clear(t),tr[rt].fa=0;
        else{
            near(1);tr[rt].fa=0,tr[rt].s[1]=tr[t].s[1];
            tr[tr[t].s[1]].fa=rt,clear(t),updsz(rt);
        }
    }
    il int sol(cs int x,cs bool l){
        insert(x);int k=near(l);
        del(x);return tr[k].vl;
    }
}
using namespace _314159_x;
int main(){
    for(ri int i=rd(),op,x;i;--i){
        op=rd(),x=rd();
        if(op==1) insert(x);
        if(op==2) del(x);
        if(op==3) wt(rnk(x));
        if(op==4) wt(xth(x));
        if(op==5) wt(sol(x,1));
        if(op==6) wt(sol(x,0));
    }
    return 0;
}

区间翻转

建树时插入一个极大值和一个极小值,节点权值表示位置
对于修改区间\([l,r]\)

  1. 查找权值为\(l\)\(r+2\)的两个节点(其实就是\(siz\)啦)
  2. 将前者旋转到根,后者旋转到根的左儿子上
  3. 要修改的区间就是根的右孩子的左子树,直接打标记即可
il void rev(int ql,int qr){
    int l=kth(ql),r=kth(qr+2);
    spl(l,0),spl(r,l);
    t[t[t[rot].s[1]].s[0]].lz^=1;
    return;
}

因为是旋转到指定位置,所以\(Splay\)有一点改动

il void spl(int x,int gl){
    for(ri int i;(i=t[x].f)!=gl;rtt(x)){
        if(t[i].f!=gl) (son(i)==son(x))?rtt(i):rtt(x);
    }
    if(!gl) rot=x;
    return;
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
cs int N=1e5+5,inf=1e9+7;
int n,m,arr[N];
namespace x314{
    int rot,id;
    struct qwq{
        int s[2],lz,sz,vl,f;
    }t[N];
    il void upd(int rt){
        t[rt].sz=t[t[rt].s[0]].sz+t[t[rt].s[1]].sz+1;
        return;
    }
    il void pdl(int rt){
        if(t[rt].lz){
            if(t[rt].s[0]) t[t[rt].s[0]].lz^=1;
            if(t[rt].s[1]) t[t[rt].s[1]].lz^=1;
            swap(t[rt].s[0],t[rt].s[1]);
            t[rt].lz^=1;
        }
        return;
    }
    il bool son(int rt){
        return (rt==t[t[rt].f].s[1]);
    }
    il void rtt(int rt){
        int o=t[rt].f,o2=t[o].f;
        if(!o) return;
        bool d=son(rt);
        t[rt].f=o2;
        if(o2) t[o2].s[son(o)]=rt;
        t[o].s[d]=t[rt].s[d^1];
        if(t[rt].s[d^1]) t[t[rt].s[d^1]].f=o;
        t[rt].s[d^1]=o,t[o].f=rt;
        return upd(o),upd(rt);
    }
    il void spl(int x,int gl){
        for(ri int i;(i=t[x].f)!=gl;rtt(x)){
            if(t[i].f!=gl) (son(i)==son(x))?rtt(i):rtt(x);
        }
        if(!gl) rot=x;
        return;
    }
    il int kth(int k){
        ri int i=rot;
        while(i){
            pdl(i);//!!!!!!
            if(k<=t[t[i].s[0]].sz) i=t[i].s[0];
            else{
                k-=t[t[i].s[0]].sz+1;
                if(k<=0) return i;
                i=t[i].s[1];
            }
        }
        return i;
    }
    il int bui(int l,int r,int fa){
        if(l>r) return 0;
        int mid=(l+r)>>1;
        int i=++id;//!!!!!!!!!
        t[i].vl=mid,t[i].f=fa;
        t[i].sz=1,t[i].lz=0;
        t[i].s[0]=bui(l,mid-1,i);
        t[i].s[1]=bui(mid+1,r,i);
        return upd(i),i;	
    }
    il void rev(int ql,int qr){
        int l=kth(ql),r=kth(qr+2);
        spl(l,0),spl(r,l);
        t[t[t[rot].s[1]].s[0]].lz^=1;
        return;
    }
    il void prt(int i){
        pdl(i);
        if(t[i].s[0]) prt(t[i].s[0]);
        if(t[i].vl&&t[i].vl<=n) cout<<t[i].vl<<' ';
        if(t[i].s[1]) prt(t[i].s[1]);
        return;
    }
} using namespace x314;
signed main(){
    cin>>n>>m;
    rot=bui(0,n+1,0);
    for(ri int i=1,l,r;i<=m;++i){
        cin>>l>>r,rev(l,r);
    }
    prt(rot);
    return 0;
} 

edit

posted @ 2022-11-08 22:02  雨夜风月  阅读(79)  评论(0编辑  收藏  举报