FHQ-Treap P3369 【模板】普通平衡樹
Treap是 二叉搜尋樹(BST)和二叉堆(Heap)的結合。二叉搜尋樹支援Treap的所有一般功能,例如查排名,查第k大,前驅,後繼,刪除,插入。它的特點是左子樹小於等於根,右子樹大於等於根。但是它的複雜度依賴於樹的高度,而樹的高度很容易被資料卡成鏈。
Heap是一種完全二叉樹,它的樹高為log(n)。一般有小根堆和大根堆。我們以小根堆為例,它的特點是根節點小於等於子節點。
其實到這裡我們會發現兩種樹的性質是矛盾的,BST要求左兒子小於等於根,右兒子大於等於根,Heap要求根既小於等於左兒子,又小於等於右兒子,同時人為規定左兒子小於右兒子。
所以為了讓二者結合,我們給二叉搜尋樹的每個節點賦予一個隨機值,通過這個隨機值來維護堆的性質。
首先我們要知道,BST的中序遍歷是不變的(最小的在最左邊),而Heap的後序遍歷是不變的(最大的在最右邊)。資料是出題人給的,也就是我們無法改變中序遍歷。現在假設我們的隨機陣列是B,那麼根據這個陣列可以得到唯一的後序遍歷,有了後序遍歷和中序遍歷我們就可以唯一的確定一棵樹。Treap一個重要的特性就是樹的形狀是確定的。在隨機的情況下,樹的高度是log(n)的。證明方法就不展開講了。
Treap是可以通過Zig和Zag來維護的,但是今天講的是利用分裂還有合併來保持平衡,單次操作的期望是log(N)。
宣告一些變數
const int N = 2e6+5;
int val[N], ch[N][2], siz[N], rnd[N], q[N];
int root, cnt, x, y, z, an, tot;
分裂是按照權值來分,(其實也可以按照size來分,一般做區間問題的時侯會用到)。把樹分為兩個子樹,其中左子樹小於等於a,右子樹大於a。
void split(int rt, int a, int &x, int &y) {
if(!rt) x = y = 0;
else {
if(val[rt] <= a) x = rt, split(ch[rt][1], a, ch[rt][1], y);
else y = rt, split(ch[rt][0], a, x, ch[rt][0]);
up(rt);
}
}
合併是按照隨機數數合併的,一定要注意順序,x一定是更小節點的根。然後按照堆的性質來合併即可
int merge(int x, int y) {
if(!x || !y) return x + y;
if(rnd[x] < rnd[y]) {
ch[x][1] = merge(ch[x][1], y);up(x); return x;
} else {
ch[y][0] = merge(x, ch[y][0]);up(y); return y;
}
}
void insert(int a) {// 插入
split(root, a, x, y);
root = merge(merge(x, new_node(a)), y);
}
void del(int a) { // 刪除,加入了垃圾回收
split(root, a, x, y);
split(x, a-1, x, z);
q[++tot] = z;
z = merge(ch[z][0], ch[z][1]);
root = merge(merge(x, z), y);
}
int _rank(int a) {//查詢a的排名
split(root, a-1, x, y);
an = siz[x] + 1;
root = merge(x, y);
return an;
}
int _kth(int rt, int k) {// 查詢排名為k的數
while(rt) {
if(k <= siz[ch[rt][0]]) rt = ch[rt][0];
else if(k == siz[ch[rt][0]] + 1) return val[rt];
else k -= siz[ch[rt][0]]+1, rt = ch[rt][1];
}
}
int pre(int a) {// 查詢前驅
split(root, a-1, x, y);
an = _kth(x, siz[x]);
root = merge(x, y);
return an;
}
int nxt(int a) { // 查詢後繼
split(root, a, x, y);
an = _kth(y, 1);
root = merge(x, y);
return an;
}
完整程式碼
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
template <typename T> inline void read(T &x) {
x = 0; T f = 1;char s = getchar();
for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
for(; isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
x *= f;
}
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define _srep(n,m,i)for (register int i = (n); i >= (m); i--)
#define _sfor(n,m,i)for (register int i = (n); i > (m); i--)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
const int N = 2e6+5;
int val[N], ch[N][2], siz[N], rnd[N], q[N];
int root, cnt, x, y, z, an, tot;
void up(int rt) {
siz[rt] = 1 + siz[ch[rt][0]] + siz[ch[rt][1]];
}
int new_node(int x) {
int new_cnt = tot ? q[tot--] : ++cnt;
val[new_cnt] = x;
siz[new_cnt] = 1;
rnd[new_cnt] = rand();
ch[new_cnt][0] = ch[new_cnt][1] = 0;
// cout << "debug: " << new_cnt << endl;
return new_cnt;
}
void split(int rt, int a, int &x, int &y) {
if(!rt) x = y = 0;
else {
if(val[rt] <= a) x = rt, split(ch[rt][1], a, ch[rt][1], y);
else y = rt, split(ch[rt][0], a, x, ch[rt][0]);
up(rt);
}
}
int merge(int x, int y) {
if(!x || !y) return x + y;
if(rnd[x] < rnd[y]) {
ch[x][1] = merge(ch[x][1], y);up(x); return x;
} else {
ch[y][0] = merge(x, ch[y][0]);up(y); return y;
}
}
void insert(int a) {
split(root, a, x, y);
root = merge(merge(x, new_node(a)), y);
}
void del(int a) {
split(root, a, x, y);
split(x, a-1, x, z);
q[++tot] = z;
z = merge(ch[z][0], ch[z][1]);
root = merge(merge(x, z), y);
}
int _rank(int a) {
split(root, a-1, x, y);
an = siz[x] + 1;
root = merge(x, y);
return an;
}
int _kth(int rt, int k) {
while(rt) {
if(k <= siz[ch[rt][0]]) rt = ch[rt][0];
else if(k == siz[ch[rt][0]] + 1) return val[rt];
else k -= siz[ch[rt][0]]+1, rt = ch[rt][1];
}
}
int pre(int a) {
split(root, a-1, x, y);
an = _kth(x, siz[x]);
root = merge(x, y);
return an;
}
int nxt(int a) {
split(root, a, x, y);
an = _kth(y, 1);
root = merge(x, y);
return an;
}
int main() {
srand(time(0));
int n,m, op, a, la = 0, ans = 0; read(n); read(m);
while(n--) {
read(a); insert(a);
}
while(m--) {
read(op); read(a);
a ^= la;
if(op == 1) insert(a);
else if(op == 2) del(a);
else if(op == 3) la = _rank(a),ans ^= la;
else if(op == 4) la = _kth(root, a), ans ^= la;
else if(op == 5) la = pre(a), ans ^= la;
else la = nxt(a), ans ^= la;
}
printf("%d\n", ans);
}
相關文章
- P3369 【模板】普通平衡樹(treap)
- P3369 【模板】普通平衡樹 —— Treap FHQtreapQT
- 洛谷P3369 普通平衡樹(Splay)
- 洛谷P3369 普通平衡樹之板子
- P6136 【模板】普通平衡樹(資料加強版)
- FHQ-treap模板
- P3380 【模板】二逼平衡樹(樹套樹)
- P3391 【模板】文藝平衡樹
- P3391 【模板】文藝平衡樹(Splay)
- 普通平衡樹學習筆記之Splay演算法筆記演算法
- 平衡樹
- 文藝平衡樹
- 平衡查詢樹
- fhq-treap
- 平衡二叉樹二叉樹
- 平衡二叉樹,B樹,B+樹二叉樹
- 平衡樹學習筆記筆記
- 平衡樹 學習筆記筆記
- 平衡二叉樹(AVL)二叉樹
- 平衡樹和二叉樹的區別二叉樹
- 平衡二叉樹(AVL樹),原來如此!!!二叉樹
- 平衡二叉查詢樹:紅黑樹
- 十三、Mysql之平衡二叉樹(AVL樹)MySql二叉樹
- 主席樹模板
- 110. 平衡二叉樹二叉樹
- Java實現紅黑樹(平衡二叉樹)Java二叉樹
- 排序二叉樹和平衡二叉樹排序二叉樹
- 平衡二叉樹(AVL樹)和 二叉排序樹轉化為平衡二叉樹 及C語言實現二叉樹排序C語言
- 線段樹模板
- 二叉堆、BST 與平衡樹
- LeetCode-110-平衡二叉樹LeetCode二叉樹
- JZ-039-平衡二叉樹二叉樹
- 手擼二叉樹——AVL平衡二叉樹二叉樹
- 自動平衡二叉樹的構建-AVL樹二叉樹
- BZOJ 3196 Tyvj 1730 二逼平衡樹:線段樹套splay
- 二叉樹的深度、寬度遍歷及平衡樹二叉樹
- Java 樹結構實際應用 四(平衡二叉樹/AVL樹)Java二叉樹
- 劃分樹模板+模板題--hdu4251