普通平衡树
题目链接:luogu P3369
题目大意
平衡树模板题,要求维护一些操作。
插入一个数,删除一个数,查询一个数的排名,查询排名一直的数,找前驱后继。
思路
写 fhq Treap 之前突然发现自己剪贴板里面有个半年前写完的 Treap 没写题解,又发现没有一道 Treap 的题解,然后就过来补题解了。
有旋 Treap 其实旋转和 splay 差不多,但只是旋转的条件和 splay 不一样,splay 是看你询问的点,而 Treap 是直接用随机数给每个点一个优先级,然后维护一个以这个优先级为关键字的大根堆。
那其实就只有旋转和点的操作会不同,其它的操作和其他一般的平衡树都挺像的。
具体实现看代码吧。
代码
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
int n, root, op, x, tot, way, new_root;
int key[100001], size[100001], sum[100001], son[100001][2], rd[100001];
void get_size(int x) {
size[x] = size[son[x][0]] + size[son[x][1]] + sum[x];
return ;
}
void turn(int &root, int way) {//旋转
new_root = son[root][way ^ 1];
son[root][way ^ 1] = son[new_root][way];
son[new_root][way] = root;
get_size(root);
get_size(new_root);
root = new_root;
return ;
}
void push_num(int &root, int x) {
if (!root) {
root = ++tot;
key[root] = x;
size[root] = 1;
sum[root] = 1;
rd[root] = rand();//随机给一个用来比较的值
return ;
}
if (x == key[root]) {
size[root]++;
sum[root]++;
return ;
}
way = x > key[root];
push_num(son[root][way], x);
if (rd[root] < rd[son[root][way]])//如果不符合就要转
turn(root, way ^ 1);
get_size(root);
return ;
}
void delete_num(int &root, int x) {//转到最上面再删
if (!root) return ;
if (key[root] != x) {
way = x > key[root];
delete_num(son[root][way], x);
get_size(root);
return ;
}
if (!son[root][0] && !son[root][1]) {
sum[root]--;
size[root]--;
if (sum[root] == 0) root = 0;
get_size(root);
return ;
}
if (son[root][0] && !son[root][1]) {
turn(root, 1);
delete_num(son[root][1], x);
get_size(root);
return ;
}
if (!son[root][0] && son[root][1]) {
turn(root, 0);
delete_num(son[root][0], x);
get_size(root);
return ;
}
if (son[root][0] && son[root][1]) {
way = (rd[son[root][0]] < rd[son[root][1]]) ^ 1;
turn(root, way);
delete_num(son[root][way], x);
get_size(root);
return ;
}
return ;
}
int find_num(int root, int x) {//像普通平衡树的做法(后面都跟一般的平衡树差不多)
if (!root) return 0;
if (x == key[root]) return size[son[root][0]] + 1;
if (x < key[root]) return find_num(son[root][0], x);
if (x > key[root]) return size[son[root][0]] + sum[root] + find_num(son[root][1], x);
}
int find_key(int root, int x) {
if (!root) return 0;
if (x <= size[son[root][0]]) return find_key(son[root][0], x);
if (x <= size[son[root][0]] + sum[root]) return key[root];
if (x > size[son[root][0]] + sum[root]) return find_key(son[root][1], x - size[son[root][0]] - sum[root]);
}
int pre(int root, int x) {
if (!root) return -2147483647;
if (x <= key[root]) return pre(son[root][0], x);
if (x > key[root]) return max(pre(son[root][1], x), key[root]);
}
int suf(int root, int x) {
if (!root) return 2147483647;
if (x >= key[root]) return suf(son[root][1], x);
if (x < key[root]) return min(suf(son[root][0], x), key[root]);
}
int main() {
srand(114514);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d %d", &op, &x);
if (op == 1) {
push_num(root, x);
continue;
}
if (op == 2) {
delete_num(root, x);
continue;
}
if (op == 3) {
printf("%d
", find_num(root, x));
continue;
}
if (op == 4) {
printf("%d
", find_key(root, x));
continue;
}
if (op == 5) {
printf("%d
", pre(root, x));
continue;
}
if (op == 6) {
printf("%d
", suf(root, x));
continue;
}
}
return 0;
}