【bzoj3224】普通平衡树(splay板子)

Description

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

Input

    第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output

    对于操作3,4,5,6每行输出一个数,表示对应答案

Sample Input

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

Sample Output

106465
84185
492737

I think

    代码参考(基本是二者的混合体qvq):
    史上最详尽的平衡树(splay)讲解与模板
    Splay伸展树学习笔记

    splay板子……
    稍微一提的是求前驱(后继),相当于新插入一个节点(顺便就旋至根节点啦),找根节点的左(右)子树中val最大(小)的节点,找到之后再删去该新建节点。
    对于删除节点,可利用find()函数(查询x数的排名)将目标点旋至根节点,通过判断其子树情况分类处理。这里具体写写两颗子树均不为空的删除操作。
    找到被删除节点x的左子树中val最大的节点y,并将其旋至x左子树根,由于y的val值大于左子树中任意其他节点,因此y一定没有右子树。那么将原x的右子树接至y的右子树,并将相关信息修改好就ok啦。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int sm = 1e5+5;

int u,n,m,sz,opt,rt;
int f[sm],c[sm],cnt[sm],size[sm],ch[sm][2];
char a;
void read(int &x) {
    x=0;a=getchar();u=1;
    while(a>'9'||a<'0') {
        if(a=='-')u=-1;a=getchar();
    }
    while(a>='0'&&a<='9')x=x*10+a-'0',a=getchar();
    x*=u;
}
bool son(int p,int fa) {//判断p是fa的左子树还是右子树
    return ch[fa][1]==p;
}
void clear(int p) {//删除点p的各项信息
    f[p]=ch[p][0]=ch[p][1]=size[p]=cnt[p]=c[p]=0;
}
void newnode(int val,int fa) {//给fa创建一个新的子节点,记得在调用之后更新fa的ch
    ++sz;f[sz]=fa;
    ch[sz][0]=ch[sz][1]=0;
    size[sz]=1;cnt[sz]=1;c[sz]=val;
}
void update(int x) {//更新以x为根的子树大小
    if(x) {
        size[x]=cnt[x];
        if(ch[x][0])size[x]+=size[ch[x][0]];
        if(ch[x][1])size[x]+=size[ch[x][1]];
    }
}
void rotate(int x) {//把x旋至其父亲
    int fa=f[x],gf=f[fa];
    int a=son(x,fa),b=!a;
    ch[fa][a]=ch[x][b];
    if(ch[x][b])f[ch[x][b]]=fa;
    ch[x][b]=fa,f[fa]=x;
    f[x]=gf;
    if(gf)ch[gf][son(fa,gf)]=x;
    else rt=x;
    update(fa);update(x);
}
void splay(int x,int p) {//将x旋至p的子节点
    while(f[x]!=p) {
        int fa=f[x],gf=f[fa];
        if(p==gf)rotate(x);
        else {
            if(son(x,fa)^son(fa,gf))
                rotate(x),rotate(x);
            else rotate(fa),rotate(x);
        }
    }
}
void insert(int val) {//插入一个值为val的新节点
    if(!rt){ newnode(val,0);rt=sz;return;}
    int now=rt,fa=f[now];
    while(now) {
        if(c[now]==val) {
            splay(now,0);cnt[now]++;return;//记得将节点旋至根
        }
        fa=now;
        now=ch[fa][val>c[fa]];
        if(!now) {
            newnode(val,fa);
            ch[fa][val>c[fa]]=sz;
            update(fa);splay(sz,0);return;//记得将节点旋至根
        }
    }
}
int find(int val) {//查询值为val的点的排名
    int ans=0,now=rt;
    while(now) {
        if(val<c[now])
            now=ch[now][0];
        else {
            ans+=(ch[now][0]?size[ch[now][0]]:0);
            if(val==c[now]) { splay(now,0);return ans+1;}
            ans+=cnt[now];
            now=ch[now][val>c[now]];
        }
    }
}
int findx(int p) {//查询排名为p的数
    int ans=0,now=rt;
    while(now) {
        if(ch[now][0]&&p<=size[ch[now][0]])
            now=ch[now][0];
        else {
            int tmp=(ch[now][0]?size[ch[now][0]]:0)+cnt[now];
            if(p<=tmp)return c[now];
            p-=tmp;
            now=ch[now][1];
        }
    }
}
int pre() {//求前驱
    int now=ch[rt][0];
    while(ch[now][1])
        now=ch[now][1];
    return now;
}
int nxt() {//求后继
    int now=ch[rt][1];
    while(ch[now][0])
        now=ch[now][0];
    return now;
}
void del(int val) {//删除值为val的点
    find(val);
    if(cnt[rt]>1){--cnt[rt];return;}
    if(!ch[rt][0]&&!ch[rt][1]){ clear(rt);return;}
    else if(!ch[rt][0]) {
        int t=rt;rt=ch[rt][1];f[rt]=0;clear(t);return;
    }
    else if(!ch[rt][1]) {
        int t=rt;rt=ch[rt][0];f[rt]=0;clear(t);return;
    }
    int l=pre();
    splay(l,rt);
    f[ch[rt][1]]=l;
    ch[l][1]=ch[rt][1];
    f[l]=0;clear(rt);
    rt=l;update(rt);
}
int main() {
    read(n);
    while(n--) {
        read(opt);read(m);
        if(opt==1)insert(m);
        else if(opt==2)del(m);
        else if(opt==3)printf("%d\n",find(m));
        else if(opt==4)printf("%d\n",findx(m));
        else {
            insert(m);
            if(opt==5)printf("%d\n",c[pre()]);
            else printf("%d\n",c[nxt()]);
            del(m);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值