洛谷P2596 [ZJOI2006]书架(BZOJ1861)

这是一篇关于使用平衡树,特别是Splay解决洛谷P2596和BZOJ1861书架问题的博客。作者通过初始的错误思路,最终理解了这是一个平衡树维护序列的题目,并给出了正确的解题方法:Top操作、Bottom操作、Insert操作和Ask操作的详细说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

平衡树

洛谷题目传送门
BZOJ题目传送门

初始想法:

对于每个节点,新增一个变量表示当前节点的优先级,用Splay维护。
置顶/置底时先把节点取出,把优先级修改到最大/最小,再插入。
放回时把两个节点取出,交换优先级后插入。
询问的话直接做就好了。

然而打了一个下午没打出来。。。实在太烦了。。。
这时候ZZK飘了过来,看了一眼:“这不是平衡树维护序列裸题吗”
Orz

正解:

Top:把x Splay到根,然后把x的左子树合并到x的后继上。
Bottom:把x Splay到根,然后把x的右子树合并到x的前驱上。
Insert:直接交换x与其前驱/后继的信息。
Ask:把x Splay到根,答案就是它的左子树大小。
Query:直接找就好了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 80000
using namespace std;
struct node{
    int w,size,to[2],fa;
}t[N*2+5];
int n,m,rt,nd,id[N+5],num[N+5];
char s[10];
void updt(int x){
    t[x].size=t[t[x].to[0]].size+t[t[x].to[1]].size+1;
    id[num[t[x].to[0]]]=t[x].to[0],id[num[t[x].to[1]]]=t[x].to[1];
}
void rtt(int x,int &w){
    int y=t[x].fa,z=t[y].fa,p=(t[y].to[0]!=x);
    if (y==w) w=x;
    else t[z].to[(t[z].to[0]!=y)]=x;
    t[x].fa=z,t[y].fa=x,t[t[x].to[p^1]].fa=y;
    t[y].to[p]=t[x].to[p^1],t[x].to[p^1]=y;
    updt(y),updt(x);
}
void splay(int x,int &w){
    while (x!=w){
        int y=t[x].fa,z=t[y].fa;
        if (y!=w)
            if ((t[y].to[0]==x)^(t[z].to[0]==y)) rtt(x,w);
            else rtt(y,w);
        rtt(x,w);
    }
    id[num[x]]=x;
}
void nsrt(int x){
    num[++nd]=x,t[nd].size=1,id[x]=nd,t[nd].to[0]=t[nd].to[1]=0;
    if (nd>1) t[nd-1].to[1]=nd,t[nd].fa=nd-1,splay(nd,rt);
}
int srch(int x,int w){
    int p=t[x].to[0];
    if (t[p].size+1==w) return x;
    if (t[p].size>=w) return srch(p,w);
    return srch(t[x].to[1],w-t[p].size-1);
}
void tp(int x){
    x=id[x],splay(x,rt);
    if (!t[x].to[0]) return;
    if (!t[x].to[1]) t[x].to[1]=t[x].to[0],t[x].to[0]=0;
    else{
        int p=srch(rt,t[t[x].to[0]].size+2);
        t[t[rt].to[0]].fa=p,t[p].to[0]=t[rt].to[0];
        t[rt].to[0]=0,splay(p,rt);
    }
}
void bttm(int x){
    x=id[x],splay(x,rt);
    if (!t[x].to[1]) return;
    if (!t[x].to[0]) t[x].to[0]=t[x].to[1],t[x].to[1]=0;
    else{
        int p=srch(rt,t[t[x].to[0]].size);
        t[t[rt].to[1]].fa=p,t[p].to[1]=t[rt].to[1];
        t[rt].to[1]=0,splay(p,rt);
    }
}
void ns(int x,int w){
    if (!w) return; splay(id[x],rt); w++;
    int p=srch(rt,t[t[id[x]].to[0]].size+w);
    int x1=num[p],x2=id[x];
    swap(id[x],id[x1]);
    swap(num[x2],num[p]);
}
int main(){
    scanf("%d%d",&n,&m); rt=1;
    for (int x,i=1;i<=n;i++)
        scanf("%d",&x),nsrt(x);
    for (int x,p,i=1;i<=m;i++){
        scanf("%s%d",s,&x);
        switch (s[0]){
            case 'T': tp(x); break;
            case 'B': bttm(x); break;
            case 'I': scanf("%d",&p),ns(x,p); break;
            case 'A': x=id[x],splay(x,rt),printf("%d\n",t[t[x].to[0]].size); break;
            case 'Q': printf("%d\n",num[srch(rt,x)]); break;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值