3224: Tyvj 1728 普通平衡树 (Splay)

本文介绍了一种高效的数据结构——伸展树(Splay Tree),并详细展示了其在插入、删除、查询等操作上的具体实现。伸展树能够动态调整结构,使得常用元素的操作效率更高。

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

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

HINT
1.n的数据范围:n<=100000

2.每个数的数据范围:[-2e9,2e9]
Source

这个题目,标准的就是splay的一些基本操作,直接如下模拟

小细节要考虑好啊。。。记住,树里面存放的是下标
代码如下:

#include<bits/stdc++.h>

using namespace std;
const int MAX = 100010;
const int INF = 0x7fffffff;
int ch[MAX][2],f[MAX],key[MAX];
int cnt[MAX],Size[MAX],mark[MAX];
//伸展树不需要额外的空间
int root,sz;
typedef long long ll;
inline int get(int x){
    return ch[f[x]][1] == x;
}
inline void clean(int x){
    ch[x][0] = ch[x][1] = f[x] = cnt[x] = key[x] = Size[x] = 0;
}
inline void update(int 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]];
    }
}
inline void Rotate(int x){
    int y = f[x],z = f[y];
    int kind = get(x);
    ch[y][kind] = ch[x][!kind];
    f[ch[y][kind]] = y;
    f[y] = x;ch[x][!kind] = y;
    f[x] = z;
    if(z)   ch[z][ch[z][1] == y] = x;
    update(y);update(x);
    //这里y变成儿子了,所以先更新y
}
inline void Splay(int x){
    //把x扭到根节点
    for(int fa;(fa = f[x]);Rotate(x)){
        if(f[fa])   Rotate((get(x)==get(fa))? fa:x);
    }
    root = x;
}
inline bool Insert(int x){
    if(root == 0){
        sz++;
        Size[sz] = cnt[sz] = 1;
        ch[sz][0] = ch[sz][1] = f[sz] = 0;
        root = sz;key[sz] = x;
        return true;
    }
    int now = root,fa = 0;
    while(true){
        if(key[now] == x){
            cnt[now]++;
            update(fa);
            Splay(now);
            return false;
        }
        fa = now;
        now = ch[now][x > key[now]];
        if(now == 0){
            sz++;
            Size[sz] = cnt[sz] = 1;
            ch[sz][0] = ch[sz][1] = 0;
            key[sz] = x;
            f[sz] = fa;
            ch[fa][x > key[fa]] = sz;
            Splay(sz);
            return true;
        }
    }
}
inline int QueryNumRank(int x){
    int ans = 0,now = root;
    while(true){
        if(x < key[now])
            now = ch[now][0];
        else{
            ans += ch[now][0]?Size[ch[now][0]]:0;
            if(key[now] == x){
                Splay(now);
                return ans+1;
            }
            ans += cnt[now];
            now = ch[now][1];
        }
    }
}
inline int QueryRankNum(int x){
    int now = root;
    while(true){
        if(ch[now][0] && Size[ch[now][0]] >= x)
            now = ch[now][0];
        else{
            int tmp = (ch[now][0]? Size[ch[now][0]]:0) + cnt[now];
            if(tmp >= x){
                return key[now];
            }
            x -= tmp;now = ch[now][1];
        }
    }
}
//注意这里查询,返回的是标号,不是key,因为在del中又用到这些操作了。
int QueryPre(){
    int k = ch[root][0];
    while(ch[k][1]) k = ch[k][1];
    return k;
}
int QueryNex(){
    int k = ch[root][1];
    while(ch[k][0]) k = ch[k][0];
    return k;
}
void del(int x){
    QueryNumRank(x);//先把这个数扭到根节点
    if(cnt[root] > 1)   cnt[root]--;
    else if(!ch[root][0] && !ch[root][1]){
        clean(root);
        root = 0;
    }
    else if(!ch[root][0]){
        int tmp = root;
        root = ch[root][1];
        f[root] = 0;
        clean(tmp);
    }
    else if(!ch[root][1]){
        int tmp = root;
        root = ch[root][0];
        f[root] = 0;
        clean(tmp);
    }
    else{
        int t = QueryPre();
        int tmp = root;
        Splay(t);
        ch[root][1] = ch[tmp][1];
        f[ch[tmp][1]] = root;
        clean(tmp);
        update(root);
    }
}
int main(void){
    int N,x,op;
    scanf("%d",&N);
    for(int i=1;i<=N;++i){
        scanf("%d%d",&op,&x);
        if(op == 1) Insert(x);
        else if(op == 2)    del(x);
        else if(op == 3)    printf("%d\n",QueryNumRank(x));
        else if(op == 4)    printf("%d\n",QueryRankNum(x));
        else if(op == 5){
            Insert(x);
            printf("%d\n",key[QueryPre()]);
            del(x);
        }
        else{
            Insert(x);
            printf("%d\n",key[QueryNex()]);
            del(x);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值