[ZJOI2006]书架

题目描述

初始时给定一个长为 \(n\) 的排列,支持以下操作:

  1. 将编号为 \(s\) 的元素放在序列首部
  2. 将编号为 \(s\) 的元素放在序列尾部
  3. 给定 \(t\) 表示将编号为 \(s\) 的元素往前或往后移动一位
  4. 查询编号为 \(s\) 的元素前面有多少个数
  5. 查询序列中第 \(s\) 个的元素的编号是多少

解法

显然是平衡树。

本题的重点在于如何找出编号 \(s\) 的元素在序列中的位置,如果知道了这个之后其它的东西都可以维护。

而我们其实可以多记录一个 \(fa\) 来实现。知道了 \(fa\) 之后,我们可以不断地往上跳同时累加上左子树大小。最后得到的便是比 \(s\) 位置小的数的个数+1,即这个数在序列中的位置。\(fa\)\(update\) 的时候更新一下即可。

inline int getpos(int id){
    int tmp=sz[lid]+1;
    while(fa[id]){
        if(id==s[fa[id]][1])
            tmp+=sz[s[fa[id]][0]]+1;
        id=fa[id];
    }
    return tmp;
}

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define N 100007
#define lid s[id][0]
#define rid s[id][1]

inline int read(){
    int x=0,flag=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

int cnt=0,key[N],s[N][2],val[N],sz[N],pos[N],fa[N];

inline void update(int id){
    sz[id]=1;
    if(lid) sz[id]+=sz[lid],fa[lid]=id;
    if(rid) sz[id]+=sz[rid],fa[rid]=id;
}

inline int New(int x){
    key[++cnt]=rand();
    sz[cnt]=1;
    s[cnt][0]=s[cnt][1]=0;
    fa[cnt]=0; 
    val[cnt]=x;
    return cnt;
}

void split(int id,int k,int &x,int &y){
    if(!id) x=y=0;
    else{
        if(sz[s[id][0]]+1<=k)
            x=id,split(rid,k-sz[s[id][0]]-1,rid,y);
        else
            y=id,split(lid,k,x,lid);
        update(id);
    }
}

int merge(int x,int y){
    if(!x||!y) return x+y;
    if(key[x]<key[y]){
        s[x][1]=merge(s[x][1],y);
        update(x);
        return x;
    }else{
        s[y][0]=merge(x,s[y][0]);
        update(y);
        return y;
    }
}

inline int getpos(int id){
    int tmp=sz[lid]+1;
    while(fa[id]){
        if(id==s[fa[id]][1])
            tmp+=sz[s[fa[id]][0]]+1;
        id=fa[id];
    }
    return tmp;
}

inline int kth(int id,int k){
    while(id){
        if(sz[lid]>=k) id=lid;
        else if(sz[lid]+1==k) return id;
        else k-=sz[lid]+1,id=rid;
    }
}

int n,m;
int main(){
    srand(time(NULL));
    n=read(),m=read();
    int rt=0,x,y,z;
    for(int i=1;i<=n;i++){
        int x=read();
        rt=merge(rt,pos[x]=New(x));
    }
    char op[10]={0};
    while(m--){
        scanf("%s",op),x=read();
        if(op[0]=='T'){
            x=getpos(pos[x]);
            split(rt,x,y,z);
            split(y,x-1,x,y);
            rt=merge(merge(y,x),z);
        }else if(op[0]=='B'){
            x=getpos(pos[x]);
            split(rt,x,y,z);
            split(y,x-1,x,y);
            rt=merge(merge(x,z),y);
        }else if(op[0]=='I'){
            int t=read();
            x=getpos(pos[x]),t+=x-1;
            split(rt,x,y,z);
            split(y,x-1,x,y);
            x=merge(x,z);
            split(x,t,x,z);
            rt=merge(merge(x,y),z);
        }else if(op[0]=='A') printf("%d\n",getpos(pos[x])-1);
        else printf("%d\n",val[kth(rt,x)]);
    }
}
posted @ 2020-10-23 22:08  Kreap  阅读(101)  评论(0)    收藏  举报