[CODEVS1343][HNOI]蚱蜢(平衡树splay)

题目描述

传送门

题解

在线段树即将写吐之际改了平衡树,没想到吃屎程度不减反增。
调了一晚上,想不出哪里出错了,最终的原因是一个小地方。
思路是按照位置存储,每一次找位置(find) ,维护maxn。求区间的时候将区间的前驱和后继splay到根和根的右儿子,添加的时候把要插在两个结点中间的两个结点splay到根和根的右儿子,删除的时候把结点的前驱和后继splay到根和根的右儿子,这样我们就可以对根的右儿子的左儿子进行操作来达到目的。
要注意的是,蚱蜢跳啊跳时insert和del的顺序,这个很重要,因为在平衡树中是按照位置存储的,每一次需要find到它的位置,del之后位置就不存在了。

代码

#include
#include
#include
using namespace std;

const int max_n=2e5+5;
const int INF=2e9;

int n,m,st,cnt;
int a[max_n];
int root,sz;
int ch[max_n][2],f[max_n],size[max_n],maxn[max_n],key[max_n];

inline bool get(int x){
    return ch[ f[x] ][1]==x;
}

inline void update(int x){
    if (x){
        size[x]=1;
        maxn[x]=key[x];
        if (ch[x][0]){
            size[x]+=size[ ch[x][0] ];
            maxn[x]=max(maxn[x],maxn[ ch[x][0] ]);
        }
        if (ch[x][1]){
            size[x]+=size[ ch[x][1] ];
            maxn[x]=max(maxn[x],maxn[ ch[x][1] ]);
        }
    }
}

inline void rotate(int x){
    int old=f[x],oldf=f[old],which=get(x);

    ch[old][which]=ch[x][which^1];
    f[ ch[old][which] ]=old;

    ch[x][which^1]=old;
    f[old]=x;

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

    update(old);
    update(x);
}

inline void splay(int x,int tar){
    for (int fa;(fa=f[x])!=tar;rotate(x))
      if (f[fa]!=tar)
        rotate( (get(x)==get(fa)) ?fa:x );
    if (!tar) root=x;
}

inline int build(int l,int r,int fa){
    if (l>r) return 0;
    int mid=(l+r)>>1;

    int now=++sz;
    key[now]=a[mid]; maxn[now]=a[mid]; size[now]=1; f[now]=fa;
    int lch=build(l,mid-1,now);
    int rch=build(mid+1,r,now);
    ch[now][0]=lch;
    ch[now][1]=rch;

    update(now);
    return now;
}

//Find the xth number
inline int find(int x){
    int now=root;
    while (1){
        if (ch[now][0]&&x<=size[ ch[now][0] ])
          now=ch[now][0];

        else{
            int temp=1;
            if (ch[now][0])
              temp+=size[ ch[now][0] ];
            if (x<=temp) return now;
            x-=temp;
            now=ch[now][1];
        }
    }
}

//Find the xth number's val
inline int findval(int x){
    int now=root;
    while (1){
        if (ch[now][0]&&x<=size[ ch[now][0] ])
          now=ch[now][0];

        else{
            int temp=1;
            if (ch[now][0])
              temp+=size[ ch[now][0] ];
            if (x<=temp) return key[now];
            x-=temp;
            now=ch[now][1];
        }
    }
}

inline void Query(int L,int R){
    //L-1 R+1
    int aa=find(L);
    int bb=find(R+2);
    splay(aa,0);
    splay(bb,aa);

    int ans=maxn[ ch[ ch[root][1] ][0] ];
    printf("%d\n",ans);

    update(ch[root][1]);
    update(root);
}

//Insert val to x
inline void Insert(int x,int val){
    //x-1 x
    int aa=find(x);
    int bb=find(x+1);
    splay(aa,0);
    splay(bb,aa);

    ch[ ch[root][1] ][0]=++sz;
    f[sz]=ch[root][1];
    ch[sz][0]=ch[sz][1]=0;
    size[sz]=1;
    key[sz]=val;
    maxn[sz]=val;

    update(f[sz]);
    update(root);
}

inline void del(int x){
    //x-1 x+1
    int aa=find(x);
    int bb=find(x+2);
    splay(aa,0);
    splay(bb,aa);

    ch[ ch[root][1] ][0]=0;

    update(ch[root][1]);
    update(root);
}

int main(){
    scanf("%d%d",&n,&m);
    a[1]=-INF; a[n+2]=INF;
    for (int i=1;i<=n;++i)
      scanf("%d",&a[i+1]);

    root=build(1,n+2,0);

    for (int i=1;i<=m;++i){
        scanf("%d",&st);
        char c=getchar();
        while (c!='L'&&c!='D') c=getchar();
        scanf("%d",&cnt);

        if (c=='L'){
            //query
            int L=st-cnt;
            int R=st-1;
            Query(L,R);

            //change
            int val=findval(st+1);//st
            del(st);
            Insert(L,val);
        }
        else{
            //query
            int L=st+1;
            int R=st+cnt;
            Query(L,R);

            //change
            int val=findval(st+1);//st
            del(st);
            Insert(R,val);
        }
    }
}

总结

①添加和删除一定要想好。
②平衡树还是要继续熟悉。

你可能感兴趣的:(题解,平衡树,省选)