FHQ-treap 小結

剛看了一天的平衡樹,我就過來小結了......
只過了幾道板子題,,,

FHQ-treap的基本原理

基本原理就是用分裂(split)和合並(merge)的方式完成平衡樹的操作

兩種基本操作及其原理

好像我也不太懂 ...wtcl

split 分裂 的基本原理

Split(rt,c,&x,&y) 將以rt節點爲根的子樹按 參數 c 分裂爲兩顆子樹 x, y,並返回其編號。

以按權值分裂爲例,稱權值較小的樹爲左樹,另一棵爲右樹。

根據二叉搜索樹左小右大的性質,可確定分裂的過程:

比較根節點權值 與 給定權值的大小關係

若小於給定權值,則根及左子樹點都小於等於給定權值,將其歸入左樹中。向右子樹遞歸,繼續分裂子樹。

否則,根及右子樹點都大於等於給定權值,將其歸入右樹中。向左子樹遞歸,繼續分裂子樹。

遞歸到葉子節點退出.

split code

void split(int rt, int c, int &x, int &y) {
	if (!rt) {
		x = y = 0;
		return;
	}
	if (tree[rt].val <= c) x = rt, split(tree[rt].r, c, tree[rt].r, y);
	else y = rt, split(tree[rt].l, c, x, tree[rt].l);
	push_up(rt);
}

merge 合併的基本思路

merge(x,y) 將以x,y節點爲根的兩棵樹合併爲一棵,並返回新樹根的編號。

特別注意:合併的兩棵樹 一定是 Split 分裂獲得的兩棵樹
而且只要merge出現,前邊一定有一個split, 而且合併的子樹一定是split分裂的樹

merge code

int merge(int x, int y) {
	if (!x || !y) return x + y;
	if (tree[x].rand < tree[y].rand) {
		tree[x].r = merge(tree[x].r, y), push_up(x);
		return x;
	} else {
		tree[y].l = merge(x, tree[y].l), push_up(y);
		return y;
	}
}

基於分離和合並的平衡樹操作

插入一個數

我們先把樹分爲x,y兩部分,然後把新的節點a看做是一棵樹,先與x合併,合併完之後將合併的整體與y合併

void insert(int c) {
	split(root, c, tmp1, tmp2);
	root = merge(merge(tmp1, newnode(c)), tmp2);
}

刪除一個數

首先我們把樹分爲x和z兩部分
那麼x樹中的最大權值爲a
再把x分爲x和y兩部分。
此時x中的最大權值爲a-1,且權值爲a的節點一定是y的根節點。
然後我們可以無視y的根節點,直接把y的左右孩子合併起來,這樣就成功的刪除了根節點,
最後再把x,y,z合併起來就好

void shan(int x) {
	split(root, x, tmp1, tmp3), split(tmp1, x - 1, tmp1, tmp2);
	tmp2 = merge(tree[tmp2].l, tree[tmp2].r);
	root = merge(merge(tmp1, tmp2), tmp3);
}

查詢a的排名

我們首先按照a-1的權值把樹分開。
那麼x樹中最大的應該是a-1。
那麼a的排名就是siz[x]+1

void query_rank(int x) {
	split(root, x - 1, tmp1, tmp2);
	printf("%d\n", tree[tmp1].size + 1);
	root = merge(tmp1, tmp2);
}

查詢排名爲a的數

直接調用查找排名的函數即可,
這個函數應該比較好理解。

printf("%d\n", tree[kth(root, x)].val);

求x的前驅(前驅定義爲小於a,且最大的數)

因爲要小於a,那麼我們按照a-1的權值劃分,
x中最大的一定是<=a-1的,
所以我們直接輸出x中最大的數就好,
(這裏有一個小技巧,因爲siz儲存的是節點的數目,然後根據二叉查找樹的性質,編號最大的就是值最大的)

void query_pre(int x) {
	split(root, x - 1, tmp1, tmp2);
	printf("%d\n", tree[kth(tmp1, tree[tmp1].size)].val);
	root = merge(tmp1, tmp2);
}

求x的後繼(後繼定義爲大於x,且最小的數)

和上一個原理類似

void query_suc(int x) {
	split(root, x, tmp1, tmp2);
	printf("%d\n", tree[kth(tmp2, 1)].val);
	root = merge(tmp1, tmp2);
}

code

#include <bits/stdc++.h>
#define ll long long
#define N 100010
#define M 1010

using namespace std;
int n, tmp1, tmp2, tmp3, add_node, root;
struct node {
	int l, r, size, val, rand;
}tree[N];

int read() {
	int s = 0, f = 0; char ch = getchar();
	while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void push_up(int rt) {
	tree[rt].size = tree[tree[rt].l].size + tree[tree[rt].r].size + 1;
}

int newnode(int c) {
	add_node++;
	tree[add_node].val = c, tree[add_node].size = 1;
	tree[add_node].rand = rand();
	return add_node;
}

void split(int rt, int c, int &x, int &y) {
	if (!rt) {
		x = y = 0;
		return;
	}
	if (tree[rt].val <= c) x = rt, split(tree[rt].r, c, tree[rt].r, y);
	else y = rt, split(tree[rt].l, c, x, tree[rt].l);
	push_up(rt);
}

int merge(int x, int y) {
	if (!x || !y) return x + y;
	if (tree[x].rand < tree[y].rand) {
		tree[x].r = merge(tree[x].r, y), push_up(x);
		return x;
	} else {
		tree[y].l = merge(x, tree[y].l), push_up(y);
		return y;
	}
}

int kth(int now, int key) {
	while (1) {
		if (key <= tree[tree[now].l].size) {
			now = tree[now].l;
			continue;
		}
		if (key == tree[tree[now].l].size + 1) return now;
		key -= tree[tree[now].l].size + 1;
		now = tree[now].r;
	}
}

void insert(int c) {
	split(root, c, tmp1, tmp2);
	root = merge(merge(tmp1, newnode(c)), tmp2);
}

void shan(int x) {
	split(root, x, tmp1, tmp3), split(tmp1, x - 1, tmp1, tmp2);
	tmp2 = merge(tree[tmp2].l, tree[tmp2].r);
	root = merge(merge(tmp1, tmp2), tmp3);
}

void query_rank(int x) {
	split(root, x - 1, tmp1, tmp2);
	printf("%d\n", tree[tmp1].size + 1);
	root = merge(tmp1, tmp2);
}

void query_pre(int x) {
	split(root, x - 1, tmp1, tmp2);
	printf("%d\n", tree[kth(tmp1, tree[tmp1].size)].val);
	root = merge(tmp1, tmp2);
}

void query_suc(int x) {
	split(root, x, tmp1, tmp2);
	printf("%d\n", tree[kth(tmp2, 1)].val);
	root = merge(tmp1, tmp2);
}

int main() {
	n = read();
	for (int i = 1; i <= n; i++) {
		int opt = read(), x = read();
		if (opt == 1) insert(x);
		if (opt == 2) shan(x);
		if (opt == 3) query_rank(x);
		if (opt == 4) printf("%d\n", tree[kth(root, x)].val);
		if (opt == 5) query_pre(x);
		if (opt == 6) query_suc(x);
	}
}

幾道有手就會做的題

洛谷P3369
洛谷P3871
洛谷P2343

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章