splay总结(吹水)

前言:

由于博主太懒,只是整理了一下,主要还是借鉴别人的博文
推荐一篇:史上最详尽的平衡树(splay)讲解与模板
普通平衡树:

#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;
void R(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
void clear(int x){
    ch[x][0]=ch[x][1]=f[x]=key[x]=cnt[x]=size[x]=0;
}
int get(int x){
    return x==ch[f[x]][1];
}
void update(int x){
    if (x) size[x]=cnt[x]+size[ch[x][0]]+size[ch[x][1]];
}
void rotate(int x){
    int old=f[x],oldf=f[old],which=get(x);
    f[old]=x;ch[old][which]=ch[x][which^1];
    f[ch[old][which]]=old;ch[x][which^1]=old;
    f[x]=oldf;
    if (oldf)
        ch[oldf][ch[oldf][1]==old]=x;
    update(old);update(x);
}
void splay(int x){
    int fa;
    while(f[x]!=0){
        fa=f[x];
        if (f[fa]!=0)
        {
            if (get(fa)==get(x)) rotate(fa);
            else rotate(x);
        }
        rotate(x);
    }
    root=x;
}
void insert(int x){
    if (!sz){
        sz++;
        root=1;
        ch[1][0]=ch[1][1]=f[1]=0;
        size[1]=cnt[1]=1;
        key[1]=x;
        return;
    }
    int now=root,fa=0;
    while (1){
        if (key[now]==x){
            cnt[now]++;
            update(now);
            update(fa);
            splay(now);
            break;
        }
        fa=now;
        now=ch[fa][key[now]<x];
        if (!now){
            sz++;
            ch[sz][0]=ch[sz][1]=0;
            f[sz]=fa;
            key[sz]=x;
            size[sz]=cnt[sz]=1;
            ch[fa][key[fa]<x]=sz;
            update(fa);
            splay(sz);
            break;
        }
    }
}
int find(int x){
    int now=root,ans=0;
    while (1){
        if (x<key[now]) now=ch[now][0];
        else{
            ans+=size[ch[now][0]];
            if (key[now]==x) 
            {
                splay(now);
                return ans+1;
            }
            ans+=cnt[now];
            now=ch[now][1];
        }
    }
}
int findx(int x){
    int now=root;
    while (1){
        if (x<=size[ch[now][0]])
            now=ch[now][0];
        else{
            int t=size[ch[now][0]]+cnt[now];
            if (x<=t) return key[now];
            x-=t;
            now=ch[now][1];
        }
    }
}
int pre(){
    int now=ch[root][0];
    while (ch[now][1]) now=ch[now][1];
    return now;
}
int next(){
    int now=ch[root][1];
    while (ch[now][0]) now=ch[now][0];
    return now;
}
void del(int x){ 
    int d=find(x);
    if (cnt[root]>1){
        cnt[root]--;
        update(root);
        return;
    }
    if (!ch[root][0]&&!ch[root][1]) { sz=root=0;  clear(root); return; }
    if (!ch[root][0]){
        int old=root;
        root=ch[root][1];
        f[root]=0;
        clear(old);
        return;     
    }
    if (!ch[root][1]){
        int old=root;
        root=ch[root][0];
        f[root]=0;
        clear(old);
        return;     
    }
    int l=pre(),old=root;
    splay(l);
    ch[root][1]=ch[old][1];
    f[ch[old][1]]=root;
    clear(old);
    update(root);
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int n,opt,x;
    scanf("%d",&n);
    for (int i=1;i<=n;++i){
        R(opt),R(x);
        switch(opt){
            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;
        }
    }
    return 0;
}

众所周知splay是序列之王,那么我们怎么用它来维护序列呢?首先我们认为splay的中序遍历即为原序列,假设我们要区间l,r操作,那么我们将l-1提到root,r+1提到root的右儿子,那么r+1的左儿子对应的区间就是我们要操作的区间,一般为了防止询问1,n我们会设置两个虚拟节点。
记得在rotate和find的时候都要标记下传。
文艺平衡树:

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=100000+55;
const int inf=2147483647;
int ch[N][2],f[N],key[N],size[N],a[N],root,sz,tag[N];
void R(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int get(int x){
    return x==ch[f[x]][1];
}
void update(int x){
    if (x) size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void pushdown(int x){
    if (x&&tag[x]){
        tag[ch[x][0]]^=1;
        tag[ch[x][1]]^=1;
        swap(ch[x][0],ch[x][1]);//下传标记别忘了交换左右儿子
        tag[x]=0;
    }
}
void rotate(int x){
    pushdown(f[x]);//记得顺序
    pushdown(x);
    int old=f[x],oldf=f[old],which=get(x);
    f[old]=x;ch[old][which]=ch[x][which^1];
    f[ch[old][which]]=old;ch[x][which^1]=old;
    f[x]=oldf;
    if(oldf)
        ch[oldf][ch[oldf][1]==old]=x;
    update(old);update(x);//注意update的顺序
}
void splay(int x,int y){
    /*for (int fa;(fa=f[x])^y;rotate(x))
        if (f[fa]^y){
            rotate(get(fa)==get(x)?fa:x);
        }*/
    int fa;
    while (f[x]^y){
        fa=f[x];
        if (f[fa]^y){
            rotate(get(fa)==get(x)?fa:x);
        }
        rotate(x);
    }
    if (!y) root=x;
}
int find(int x){
    int now=root;
    while (1){
        pushdown(now);
        if (x<=size[ch[now][0]]) now=ch[now][0];
        else{
            x-=size[ch[now][0]]+1;
            if (!x) return now;
            now=ch[now][1];
        }
    }
}
int build(int l,int r,int fa){
    if (l>r) return 0;
    int m=l+r>>1,now=++sz;
    key[now]=a[m];
    f[now]=fa;
    if(l^r){
        ch[now][0]=build(l,m-1,now);
        ch[now][1]=build(m+1,r,now);
    }
    update(now);
    return now;
}
void print(int now){
    pushdown(now);
    if (ch[now][0]) print(ch[now][0]);
    if (key[now]!=inf&&key[now]!=-inf)printf("%d ",key[now]);
    if (ch[now][1]) print(ch[now][1]);
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int n,m,l,r,x,y;
    R(n);R(m);
    a[1]=-inf;
    a[n+2]=inf;
    fo(i,1,n)
        a[i+1]=i;
    root=build(1,n+2,0);
    while (m--){
        R(l);R(r);
        x=find(l);  
        y=find(r+2);
        splay(x,0); 
        splay(y,x);
        tag[ch[ch[root][1]][0]]^=1;
    }
    print(root);
    return 0;
}

jzoj4216
给出一个N个整数构成的序列,有M次操作,每次操作有一下三种:
①Insert Y X,在序列的第Y个数之前插入一个数X;
②Add L R X,对序列中第L个数到第R个数,每个数都加上X;
③Query L R,询问序列中第L个数到第R个数的平方和。
这题可以用线段树离线做,但是splay更简单。
插入的话就是,把x-1提到root,把x提到x-1的儿子,插入的这个点就是x的左儿子,平方和就维护一下区间和,区间大小,另外,在这题中,我没有建树,而是先把它,弄成一条链,接着再通过splay把它进行调整

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=200010;
const int mo=7459;
int key[N],ch[N][2],size[N],f[N],t[N],s[N],tag[N],root,sz,d[N];
void R(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int get(int x){
    return x==ch[f[x]][1];
}
void update(int x){
    size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
    s[x]=(s[ch[x][0]]+s[ch[x][1]]+key[x])%mo;
    t[x]=(t[ch[x][0]]+t[ch[x][1]]+key[x]*key[x]%mo)%mo;
}
void add(int x,int k){
    if (x){
        t[x]=(t[x]+2*s[x]%mo*k%mo+size[x]*k%mo*k%mo)%mo;
        s[x]=(s[x]+size[x]*k)%mo;
        key[x]=(key[x]+k)%mo;
        tag[x]=(tag[x]+k)%mo;
    }
}
void pushdown(int x){
    if (tag[x]){
        add(ch[x][0],tag[x]);
        add(ch[x][1],tag[x]);
        tag[x]=0;
    }
}
void rotate(int x){
    pushdown(f[x]);
    pushdown(x);
    int old=f[x],oldf=f[old],which=get(x);
    f[old]=x;ch[old][which]=ch[x][which^1];
    f[ch[old][which]]=old;ch[x][which^1]=old;
    f[x]=oldf;
    if(oldf)
        ch[oldf][ch[oldf][1]==old]=x;
    update(old);update(x);
}
void splay(int x,int y){
    for (int fa;(fa=f[x])^y;rotate(x))
        if (f[fa]^y){
            rotate(get(fa)==get(x)?fa:x);
        }
    if (!y) root=x;
}
int find(int x){
    int now=root;
    while (1){
        pushdown(now);
        if (x<=size[ch[now][0]]) now=ch[now][0];
        else{
            x-=size[ch[now][0]]+1;
            if (!x) return now;
            now=ch[now][1];
        }
    }
}
int F(int x){
    return((x%mo)+mo)%mo;
}
int main(){
    //freopen("jzoj4216.in", "r", stdin);
    //freopen("jzoj4216.out", "w", stdout);
    int n,m,x,y,l,r,v;
    char s1[10];
    R(n);
    size[1]=1;
    fo(i,1,n){
        R(key[i+1]);
        f[i]=i+1;ch[i+1][0]=i;update(i+1);
    }
    f[n+1]=sz=root=n+2;ch[n+2][0]=n+1;update(n+2);
    R(m);
    while (m--){
        scanf("%s",s1);R(x);R(y);
        if (s1[0]=='I'){
            l=find(x);r=find(x+1);
            //printf("%d %d\n",l,r);
            splay(l,0);splay(r,l);
            sz++;
            f[sz]=r;ch[r][0]=sz;size[sz]=1;key[sz]=y;
            s[sz]=y;t[sz]=y*y%mo;
            update(r);
            splay(sz,0);
        }
        if (s1[0]=='A'){
            R(v);
            l=find(x);r=find(y+2);
            //printf("%d %d\n",l,r);
            splay(l,0);
            splay(r,l);
            add(ch[r][0],v);
            //splay(ch[r][0],0);
        }
        if (s1[0]=='Q'){
            l=find(x);r=find(y+2);
            //printf("%d %d\n",l,r);
            splay(l,0);splay(r,l);
            printf("%d\n",F(t[ch[r][0]]));
        }
    }
}

后记:
splay虽然很强大,但是却也有它做不了的东西,比如优化连边(线段树),可持久化平衡树(treap),而且splay也有时会被卡常,但是还是挺好用的,膜拜tarjan。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值