【BZOJ1861】书架(Splay)

传送门

书架

I think

    思路:将每本书的编号作为其在splay中的编号,在查找点x的rank时将x转至树根左子树大小即是。
    Attention:删除一个点,找前驱后继与将右子树直接接在前驱上复杂度是大不相同的。

Code

/**************************************************************
    Problem: 1861
    User: Etta
    Language: C++
    Result: Accepted
    Time:1440 ms
    Memory:2384 kb
****************************************************************/

#include<cstdio>
const int sm = 8e4+10;
const int inf = 0x3f3f3f3f;

int N,M,Rt;
int A[sm],Fa[sm],Sz[sm],Ch[sm][2];
char str[10];

void read(int &x) {
    char ch=getchar();x=0;int f=1;
    while(ch>'9'||ch<'0') { if(ch=='-')f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    x*=f;
}
bool Son(int x,int fa) {
    return Ch[fa][1]==x;
}
void Update(int x) {
    if(x) {
        Sz[x]=1;
        if(Ch[x][0]) Sz[x]+=Sz[Ch[x][0]];
        if(Ch[x][1]) Sz[x]+=Sz[Ch[x][1]];
    }
}
void Rotate(int x) {
    int f=Fa[x],g=Fa[f];
    int a=Son(x,f),b=!a;
    Ch[f][a]=Ch[x][b];
    if(Ch[x][b]) Fa[Ch[x][b]]=f;
    Ch[x][b]=f,Fa[f]=x;
    Fa[x]=g;
    if(g) Ch[g][Son(f,g)]=x;
    else Rt=x;
    Update(f),Update(x);
}
void Splay(int x,int p) {
    while(Fa[x]!=p) {
        int f=Fa[x],g=Fa[f];
        if(g==p) Rotate(x);
        else if(Son(x,f)^Son(f,g))
            Rotate(x),Rotate(x);
        else Rotate(f),Rotate(x);
    }
}
void Build(int &rt,int l,int r) {
    if(l==r) { rt=A[l],Sz[rt]=1; return ; }
    int m=(l+r)>>1; rt=A[m];
    if(l<=m-1) Build(Ch[rt][0],l,m-1);
    if(r>=m+1) Build(Ch[rt][1],m+1,r);
    Fa[Ch[rt][0]]=Fa[Ch[rt][1]]=rt;
    Sz[rt]=Sz[Ch[rt][0]]+Sz[Ch[rt][1]]+1; 
}
int Pre() {
    int now=Ch[Rt][0];
    while(Ch[now][1]) now=Ch[now][1];
    return now;
}
void del(int x) {
    Fa[x]=Ch[x][0]=Ch[x][1]=Sz[x]=0; return;
}
int Find(int rt,int k) {
    if(Sz[Ch[rt][0]]+1==k) { Splay(rt,0);return rt; } 
    if(Sz[Ch[rt][0]]+1<k) return Find(Ch[rt][1],k-Sz[Ch[rt][0]]-1); 
    return Find(Ch[rt][0],k);
}
void DI(int x,int rnk,bool ind) {
    Splay(x,0);
    if(ind) rnk+=Sz[Ch[x][0]]+1;
    if(Ch[x][0]*Ch[x][1]==0) {
        Rt=Ch[x][0]+Ch[x][1];
        Fa[Rt]=0,del(x);
    }
    else {
        int pre=Pre(); Splay(pre,x);
        Ch[pre][1]=Ch[x][1];
        Rt=Fa[Ch[x][1]]=pre;
        Fa[Rt]=0,del(x),Update(Rt);
    }
    int a=Find(Rt,rnk-1),b=Find(Rt,rnk);
    Splay(a,0),Splay(b,a);
    Ch[b][0]=x,Fa[x]=b,Sz[x]=1;
    Ch[x][0]=Ch[x][1]=0;
    Update(b),Update(a);
}
int main() {
    read(N),read(M);
    for(int i=1;i<=N;++i) read(A[i]);
    A[0]=sm-4,A[N+1]=sm-5,Build(Rt,0,N+1);
    for(int i=1,x,y,tmp;i<=M;++i) {
        scanf("%s",str),read(x);
        if(str[0]=='T') DI(x,2,0);
        if(str[0]=='B') DI(x,N+1,0);
        if(str[0]=='I') { read(y); if(y) DI(x,y,1); }
        if(str[0]=='A') Splay(x,0),printf("%d\n",Sz[Ch[Rt][0]]-1);
        if(str[0]=='Q') printf("%d\n",Find(Rt,x+1));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值