11831 蔡嘉晉
1 2 3 4 5
1
2
3
4
5
struct Node {
Node *ch[2]; // 兩個子節點的位址
int val, rank;
int rep_cnt; // 該節點重複出現次數
int siz;
Node(int val) : val(val), rep_cnt(1), siz(1) {
ch[0] = ch[1] = nullptr;
prior = rand(); //random出一個priority供heap使用
}
void upd_siz() {
// 旋轉或刪除等操作後重新計算size
siz = rep_cnt;
if (ch[0] != nullptr) siz += ch[0]->siz;
if (ch[1] != nullptr) siz += ch[1]->siz;
}
};
2
3
1
5
4
1
2
3
4
5
2
3
1
4
5
右旋的話就相反
void rotate(Node *&cur, int dir) {//dir=0 or 1 0右旋 1左旋
Node *tmp = cur->ch[dir];
cur->ch[dir] = tmp->ch[!dir]; // 將 舊根節點 的 右子樹 變成 新根節點的左子樹
tmp->ch[!dir] = cur; // 將 新根節點 的 左子樹 變成 舊根節點及其子樹
tmp->upd_siz(), cur->upd_siz(); // 更新大小
cur = tmp; // 最后把臨時儲存 新根節點 的節點變成根結點
}
# PRESENTING CODE
void insert(Node *&cur, int val) {
if (cur == nullptr) {// 這棵treap甚至是空的
cur = new Node(val);
return;
} else if (val == cur->val) {// 如果有这个值相同的节点,就把重复数量加一
cur->rep_cnt++;
cur->siz++;
} else if (val < cur->val) { // 維護二元搜尋樹性质,val 比當前節點小就插到左邊,反之亦然
insert(cur->ch[0], val);
if (cur->ch[0]->rank < cur->rank) {
// 小根堆中,上面節點的priority一定更小
// 因為新插的左子節點比父節點小,现在需要讓左子節點變成父節點
rotate(cur, RT); // 要把左子節點转上来,需要右旋
}
cur->upd_siz(); // 插入之后大小會變化,需要更新
} else {
_insert(cur->ch[1], val);
if (cur->ch[1]->rank < cur->rank) {
_rotate(cur, LF);
}
cur->upd_siz();
}
}
void _del(Node *&cur, int val) {
if (val > cur->val) {
_del(cur->ch[1], val);
// 值更大就在右子樹,反之亦然
cur->upd_siz();
} else if (val < cur->val) {
_del(cur->ch[0], val);
cur->upd_siz();
} else {
if (cur->rep_cnt > 1) {
// 如果要刪除的節點是重複的,可以直接把重複值减小
cur->rep_cnt--, cur->siz--;
return;
}
int state = 0;
state |= (cur->ch[0] != nullptr);
state |= ((cur->ch[1] != nullptr) << 1);
// 00都無,01有左無右,10,無左有右,11都有
Node *tmp = cur;
switch (state) {
case 0:
delete cur;
cur = nullptr;
// 没有任何子節點,就直接把這個節點刪了
break;
case 1: // 有左無右
cur = tmp->ch[0];
// 把根變成左子節點,然后把原来的根節點刪除
delete tmp;
break;
case 2: // 有右無左
cur = tmp->ch[1];
delete tmp;
break;
case 3:
rot_type dir = cur->ch[0]->rank < cur->ch[1]->rank ? 0 : 1; // dir 是 rank 更小的那个儿子
_rotate(cur, dir);
_del(
cur->ch[!dir],
val); // 旋轉完成後原来的根節點就在旋方向那邊,所以需要
// 繼續把這個原来的根節點繼續遞迴下去
// 如果要刪的這個節點是在整個樹的「上層的」
// 那通過遞迴的旋轉操作,便可將他轉到沒有子樹了(或者只有一個),再刪掉它。
cur->upd_siz();
// 刪除會造成大小改變
break;
}
}
}
# PRESENTING CODE
好像已經跟treap沒關係了 正常的二元搜尋樹都行
int _query_rank(Node *cur, int val) {
int less_siz = cur->ch[0] == nullptr ? 0 : cur->ch[0]->siz;
if (val == cur->val)
return less_siz + 1;
else if (val < cur->val) {
if (cur->ch[0] != nullptr)
return _query_rank(cur->ch[0], val);
else
return 1; // 如果左子樹是空的,代表比最小的節點還要小,那這個數字就是最小的
} else {
if (cur->ch[1] != nullptr)
return less_siz + cur->rep_cnt + _query_rank(cur->ch[1], val);
else
return cur->siz + 1;
}
}
int _query_val(Node *cur, int rank) {
int less_siz = cur->ch[0] == nullptr ? 0 : cur->ch[0]->siz;
// less siz 是左子樹的大小
if (rank <= less_siz)
return _query_val(cur->ch[0], rank);
else if (rank <= less_siz + cur->rep_cnt)
return cur->val;
else
return _query_val(cur->ch[1], rank - less_siz - cur->rep_cnt);
}
// author: (ttzytt)[ttzytt.com]
#include <bits/stdc++.h>
using namespace std;
struct Node {
Node *ch[2];
int val, rank;
int rep_cnt;
int siz;
Node(int val) : val(val), rep_cnt(1), siz(1) {
ch[0] = ch[1] = nullptr;
rank = rand();
}
void upd_siz() {
siz = rep_cnt;
if (ch[0] != nullptr) siz += ch[0]->siz;
if (ch[1] != nullptr) siz += ch[1]->siz;
}
};
class Treap {
private:
Node *root;
enum rot_type { LF = 1, RT = 0 };
int q_prev_tmp = 0, q_nex_tmp = 0;
void _rotate(Node *&cur, rot_type dir) {
Node *tmp = cur->ch[dir];
cur->ch[dir] = tmp->ch[!dir];
tmp->ch[!dir] = cur;
tmp->upd_siz(), cur->upd_siz();
cur = tmp;
}
void _insert(Node *&cur, int val) {
if (cur == nullptr) {
cur = new Node(val);
return;
} else if (val == cur->val) {
cur->rep_cnt++;
cur->siz++;
} else if (val < cur->val) {
_insert(cur->ch[0], val);
if (cur->ch[0]->rank < cur->rank) {
_rotate(cur, RT);
}
cur->upd_siz();
} else {
_insert(cur->ch[1], val);
if (cur->ch[1]->rank < cur->rank) {
_rotate(cur, LF);
}
cur->upd_siz();
}
}
void _del(Node *&cur, int val) {
if (val > cur->val) {
_del(cur->ch[1], val);
cur->upd_siz();
} else if (val < cur->val) {
_del(cur->ch[0], val);
cur->upd_siz();
} else {
if (cur->rep_cnt > 1) {
cur->rep_cnt--, cur->siz--;
return;
}
uint8_t state = 0;
state |= (cur->ch[0] != nullptr);
state |= ((cur->ch[1] != nullptr) << 1);
Node *tmp = cur;
switch (state) {
case 0:
delete cur;
cur = nullptr;
break;
case 1: // 有左无右
cur = tmp->ch[0];
delete tmp;
break;
case 2: // 有右无左
cur = tmp->ch[1];
delete tmp;
break;
case 3:
rot_type dir = cur->ch[0]->rank < cur->ch[1]->rank ? RT : LF;
_rotate(cur, dir);
_del(cur->ch[!dir], val);
cur->upd_siz();
break;
}
}
}
int _query_rank(Node *cur, int val) {
int less_siz = cur->ch[0] == nullptr ? 0 : cur->ch[0]->siz;
if (val == cur->val)
return less_siz + 1;
else if (val < cur->val) {
if (cur->ch[0] != nullptr)
return _query_rank(cur->ch[0], val);
else
return 1;
} else {
if (cur->ch[1] != nullptr)
return less_siz + cur->rep_cnt + _query_rank(cur->ch[1], val);
else
return cur->siz + 1;
}
}
int _query_val(Node *cur, int rank) {
int less_siz = cur->ch[0] == nullptr ? 0 : cur->ch[0]->siz;
if (rank <= less_siz)
return _query_val(cur->ch[0], rank);
else if (rank <= less_siz + cur->rep_cnt)
return cur->val;
else
return _query_val(cur->ch[1], rank - less_siz - cur->rep_cnt);
}
int _query_prev(Node *cur, int val) {
if (val <= cur->val) {
if (cur->ch[0] != nullptr) return _query_prev(cur->ch[0], val);
} else {
q_prev_tmp = cur->val;
if (cur->ch[1] != nullptr) _query_prev(cur->ch[1], val);
return q_prev_tmp;
}
return -1145;
}
int _query_nex(Node *cur, int val) {
if (val >= cur->val) {
if (cur->ch[1] != nullptr) return _query_nex(cur->ch[1], val);
} else {
q_nex_tmp = cur->val;
if (cur->ch[0] != nullptr) _query_nex(cur->ch[0], val);
return q_nex_tmp;
}
return -1145;
}
public:
void insert(int val) { _insert(root, val); }
void del(int val) { _del(root, val); }
int query_rank(int val) { return _query_rank(root, val); }
int query_val(int rank) { return _query_val(root, rank); }
int query_prev(int val) { return _query_prev(root, val); }
int query_nex(int val) { return _query_nex(root, val); }
};
Treap tr;
int main() {
srand(0);
int t;
scanf("%d", &t);
while (t--) {
int mode;
int num;
scanf("%d%d", &mode, &num);
switch (mode) {
case 1:
tr.insert(num);
break;
case 2:
tr.del(num);
break;
case 3:
printf("%d\n", tr.query_rank(num));
break;
case 4:
printf("%d\n", tr.query_val(num));
break;
case 5:
printf("%d\n", tr.query_prev(num));
break;
case 6:
printf("%d\n", tr.query_nex(num));
break;
}
}
}
# PRESENTING CODE
感謝OI wiki不然我做不完