【luogu P3369】普通平衡树(Treap 做法)

普通平衡树

题目链接: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\n", find_num(root, x));
			continue;
		}
		if (op == 4) {
			printf("%d\n", find_key(root, x));
			continue;
		}
		if (op == 5) {
			printf("%d\n", pre(root, x));
			continue;
		}
		if (op == 6) {
			printf("%d\n", suf(root, x));
			continue;
		}
	}
	
	return 0;
}
上一篇:旋转Treap


下一篇:Treap