您的位置:首页 > 其它

[BZOJ 3224]Tyvj 1728 普通平衡树

2017-12-16 14:01 495 查看

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]

题解

作为一个模板题放在这里,准备写4个常用的平衡树。算是个坑...

1 Splay

1.1 插入操作

一般平衡树的操作,不多赘述。

1.2 删除操作

我们考虑删除数$x$:我们将$x$的前驱和后继找到。两个找到之后再将前驱通过$splay$操作旋到根,再将后继旋成前驱的儿子,删除后继的左儿子即可。
显然能够保证后继的左儿子只有一个。
至于为什么要前驱和后继都找到之后再旋转,是因为查找的时候会将其他的节点旋转到根。

1.3 查询名次

我们通过维护子树大小来实现这一功能。
某个数的名次其实就是当它旋转到根的时候左子树的大小+1。
我们对于每次插入操作,删除操作,$rotate$操作都要更新名次。

1.4 查询Kth数

递归实现。
若左子树大小+1 <= 查询名次 <= 左子树大小+根节点数个数,结果就是当前数;
若查询名次 <= 左子树大小+1,递归查询左子树;
若查询名次 >= 左子树大小+根节点数个数,将查询名次-=左子树大小+根节点数个数,递归查询右子树。

1.5 查找前驱

通过$find$操作,找到“接近”这个数的数的位置。
为什么是“接近”,是因为可能$splay$中可能不存在这个数,不存在的话我们就假想最后停在的节点就是这个数。
将其旋到根。判断这个数与根节点的数的关系,若小于(即不存在查询的数)显然这个数就是前驱。
若非,就查询其左子树“最靠右的那个数”。

1.6 查找后继

和查找前驱类似。

//It is made by Awson on 2017.12.15
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const int N = 100000;
const int INF = ~0u>>1;

struct fhq_Treap {
int ch[N+5][2], lev[N+5], size[N+5], key[N+5], tot, root;
queue<int>mem;
int newnode(int keyy) {
int o;
if (!mem.empty()) o = mem.front(), mem.pop();
else o = ++tot;
ch[o][0] = ch[o][1] = 0;
size[o] = 1;
key[o] = keyy, lev[o] = rand();
return o;
}
void pushup(int o) {
size[o] = size[ch[o][0]]+size[ch[o][1]]+1;
}
void split(int o, int keyy, int &x, int &y) {
if (!o) x = y = 0;
else {
if (key[o] <= keyy) x = o, split(ch[o][1], keyy, ch[o][1], y);
else y = o, split(ch[o][0], keyy, x, ch[o][0]);
pushup(o);
}
}
int merge(int x, int y) {
if (!x || !y) return x+y;
if (lev[x] < lev[y]) {
ch[x][1] = merge(ch[x][1], y);
pushup(x); return x;
}else {
ch[y][0] = merge(x, ch[y][0]);
pushup(y); return y;
}
}
void insert(int keyy) {
int r1, r2; split(root, keyy, r1, r2);
root = merge(merge(r1, newnode(keyy)), r2);
}
void delet(int keyy) {
int r1, r2, r3; split(root, keyy-1, r1, r2);
split(r2, keyy, r2, r3);
mem.push(r2);
r2 = merge(ch[r2][0], ch[r2][1]);
root = merge(merge(r1, r2), r3);
}
int rank(int keyy) {
int r1, r2; split(root, keyy-1, r1, r2);
int ans = size[r1]+1;
root = merge(r1, r2);
return ans;
}
int get_num(int o, int rk) {
if (size[ch[o][0]]+1 == rk) return key[o];
if (size[ch[o][0]] >= rk) return get_num(ch[o][0], rk);
return get_num(ch[o][1], rk-1-size[ch[o][0]]);
}
int get_pre(int keyy) {
int r1, r2; split(root, keyy-1, r1, r2);
int o = r1;
while (ch[o][1]) o = ch[o][1];
int ans = key[o];
root = merge(r1, r2);
return ans;
}
int get_nex(int keyy) {
int r1, r2; split(root, keyy, r1, r2);
int o = r2;
while (ch[o][0]) o = ch[o][0];
int ans = key[o];
root = merge(r1, r2);
return ans;
}
}T;
int n, opt, x;

void work() {
srand(time(0));
scanf("%d", &n);
while (n--) {
scanf("%d%d", &opt, &x);
if (opt == 1) T.insert(x);
else if (opt == 2) T.delet(x);
else if (opt == 3) printf("%d\n", T.rank(x));
else if (opt == 4) printf("%d\n", T.get_num(T.root, x));
else if (opt == 5) printf("%d\n", T.get_pre(x));
else printf("%d\n", T.get_nex(x));
}
}
int main() {
work();
return 0;
}


fhq_Treap
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: