P3369 【模板】普通平衡树 Treap树堆学习笔记
生活随笔
收集整理的這篇文章主要介紹了
P3369 【模板】普通平衡树 Treap树堆学习笔记
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- Treap = Tree + Heap (樹堆 = 樹 + 堆)
- 1. 例題1 洛谷P3369 【模板】普通平衡樹
- 1. 結構體定義如下
- 2. 向上更新`以u為根的樹的節點個數`
- 3. 旋轉操作 route
- 4. 插入操作 insert
- 5. 刪除操作 del
- 6. 查詢排名rand_x
- 7. 查詢 K 小值 kth
- 8. 求key的前驅 get_pre
- 9. 求key的后繼 get_suf
- 完整代碼
- 擴展應用
- 1. 線段樹套平衡樹,求區間前驅后繼排名(就是線段樹的每個節點都是一個平衡樹)
- 2. 伸展樹, 區間反轉分割
- 3. 雙端優先隊列的實現 (可以 取最大最小值的堆)
- 4. 游戲排名系統 (動態數據容器) [原文鏈接https://blog.csdn.net/yang_yulei/article/details/46005845](https://blog.csdn.net/yang_yulei/article/details/46005845)
Treap = Tree + Heap (樹堆 = 樹 + 堆)
大佬的博客https://www.luogu.com.cn/blog/HOJQVFNA/qian-xi-treap-ping-heng-shu
簡介 : 本質上是一種使用 隨機權值維護堆的性質的二叉查找樹, 通過基礎的樹旋轉維持堆的性質,
由隨機權值保證樹高度為 logN
具體做法 : 插入或刪除后, 若不滿足 堆的性質 則旋轉
優點 : 防止 退化成鏈表 簡單好寫, 是最好寫的平衡樹之一, 好理解
缺點 : 常數大
1. 例題1 洛谷P3369 【模板】普通平衡樹
您需要寫一種數據結構(可參考題目標題),來維護一些數,其中需要提供以下操作:
1. 插入 x 數 (插入值)
2. 刪除 x 數(若有多個相同的數,因只刪除一個)
3. 查詢 x 數的排名(排名定義為比當前數小的數的個數 +1 )
4. 查詢排名為 k 的數 (求K小值)
5. 求 x 的前驅 (前驅定義為小于 x,且最大的數)
6. 求 x 的后繼 (后繼定義為大于 x,且最小的數)
1. 結構體定義如下
#define lson(u) (tree[u].chl[0]) #define rson(u) (tree[u].chl[1]) struct Node {int chl[2], //chl[0]是左兒子 , chl[1]是右兒子val, //節點值sz, //以該節點的為根節點個數recy, //該節點重復了幾次rnd; //隨機優先級 } tree[MAXN<<4];2. 向上更新以u為根的樹的節點個數
3. 旋轉操作 route
4. 插入操作 insert
5. 刪除操作 del
6. 查詢排名rand_x
所以排名應加上 左兒子的節點的個數 和 根節點的重復次數
7. 查詢 K 小值 kth
8. 求key的前驅 get_pre
9. 求key的后繼 get_suf
int get_suf(int u, int key) {if(!u) return INF;if(key >= tree[u].val) return get_suf(rson(u), key);elsereturn min(tree[u].val, get_suf(lson(u), key)); }完整代碼
// #define debug #ifdef debug #include <time.h> #include "win_majiao.h" #endif#include <iostream> #include <algorithm> #include <vector> #include <string.h> #include <map> #include <set> #include <stack> #include <queue> #include <math.h>#define MAXN ((int)1e5+7) #define ll long long int #define INF (0x7f7f7f7f) #define QAQ (0)using namespace std; typedef vector<vector<int> > VVI;#define show(x...) \do { \cout << "[" << #x << " -> "; \err(x); \} while (0)void err() { cout << "]" << endl; } template<typename T, typename... A> void err(T a, A... x) { cout << a << ' '; err(x...); }namespace FastIO{char print_f[105];void read() {}void print() { putchar('\n'); }template <typename T, typename... T2>inline void read(T &x, T2 &... oth) {x = 0;char ch = getchar();ll f = 1;while (!isdigit(ch)) {if (ch == '-') f *= -1; ch = getchar();}while (isdigit(ch)) {x = x * 10 + ch - 48;ch = getchar();}x *= f;read(oth...);}template <typename T, typename... T2>inline void print(T x, T2... oth) {ll p3=-1;if(x<0) putchar('-'), x=-x;do{print_f[++p3] = x%10 + 48;} while(x/=10);while(p3>=0) putchar(print_f[p3--]);putchar(' ');print(oth...);} } // namespace FastIO using FastIO::print; using FastIO::read;int n, m, Q, K, root, tot;#define lson(u) (tree[u].chl[0]) #define rson(u) (tree[u].chl[1]) struct Node {int chl[2], //chl[0]是左兒子 , chl[1]是右兒子val, //節點值sz, //該節點的為根節點個數recy, //該節點重復了幾次rnd; //隨機優先級 } tree[MAXN<<4];void push_up(int u) {//更新節點以u為根的樹的節點個數tree[u].sz = tree[lson(u)].sz + tree[rson(u)].sz + tree[u].recy; }#define LROUTE 0 #define RROUTE 1 void route(int& u, int cmd) { //cmd=0左旋, cmd=1右旋int tmp = tree[u].chl[cmd^1];tree[u].chl[cmd^1] = tree[tmp].chl[cmd];tree[tmp].chl[cmd] = u;push_up(u), push_up(tmp);u = tmp; }void insert(int& u, int key) {if(!u) {u = ++tot;tree[u].sz = tree[u].recy = 1;tree[u].val = key;tree[u].rnd = rand(); //隨機權值return ;}tree[u].sz ++;if(key == tree[u].val) { tree[u].recy ++; return ; }int cmd = (key > tree[u].val); //大于則向右插入insert(tree[u].chl[cmd], key);//插入后維護大根堆的性質//失衡的時候, 往與插入相反的方向旋轉if(tree[u].rnd < tree[tree[u].chl[cmd]].rnd)route(u, cmd^1);push_up(u); }void del(int& u, int key) {if(!u) return ;if(key < tree[u].val) del(lson(u), key);else if(key > tree[u].val) del(rson(u), key);else { //4種刪除情況if(!lson(u) && !rson(u)) { //葉子節點直接刪除tree[u].sz --,tree[u].recy --;if(tree[u].recy == 0) u = 0;} else if(lson(u) && !rson(u)) { //只有左節點route(u, 1);del(rson(u), key);} else if(!lson(u) && rson(u)) { //只有右節點route(u, 0);del(lson(u), key);} else if(lson(u) && rson(u)) { //左右都有//把大的那個轉上來,并去另一個子樹解決int cmd = (tree[lson(u)].rnd > tree[rson(u)].rnd);route(u, cmd);del(tree[u].chl[cmd], key);}}push_up(u); }int rank_x(int u, int key) { //求key的排名if(!u) return 0;if(key == tree[u].val) //返回左子樹個數 + 1return tree[lson(u)].sz + 1;else if(key < tree[u].val) //小于就去左子樹找return rank_x(lson(u), key);else //大于就去右子樹, 類似于權值線段樹return tree[lson(u)].sz +tree[u].recy +rank_x(rson(u), key); }int kth(int u, int k) { //查詢k大值if(!u) return 0;if(tree[lson(u)].sz >= k) return kth(lson(u), k);else if(tree[lson(u)].sz + tree[u].recy < k)return kth(rson(u), k-tree[lson(u)].sz-tree[u].recy);else return tree[u].val; }int get_pre(int u, int key) {if(!u) return -INF;if(key <= tree[u].val) return get_pre(lson(u), key);elsereturn max(tree[u].val, get_pre(rson(u), key)); }int get_suf(int u, int key) {if(!u) return INF;if(key >= tree[u].val) return get_suf(rson(u), key);elsereturn min(tree[u].val, get_suf(lson(u), key)); }signed main() { #ifdef debugfreopen("test.txt", "r", stdin);clock_t stime = clock(); #endifread(n);int cmd, val, ans;for(int i=1; i<=n; i++) {read(cmd, val);switch (cmd) {case 1:insert(root, val);break;case 2:del(root, val);break;case 3:ans = rank_x(root, val);printf("%d\n", ans);break;case 4:ans = kth(root, val);printf("%d\n", ans);break;case 5:ans = get_pre(root, val);printf("%d\n", ans);break;case 6:ans = get_suf(root, val);printf("%d\n", ans);break;default:break;}} #ifdef debugclock_t etime = clock();printf("rum time: %lf 秒\n",(double) (etime-stime)/CLOCKS_PER_SEC); #endif return 0; }擴展應用
1. 線段樹套平衡樹,求區間前驅后繼排名(就是線段樹的每個節點都是一個平衡樹)
2. 伸展樹, 區間反轉分割
3. 雙端優先隊列的實現 (可以 取最大最小值的堆)
4. 游戲排名系統 (動態數據容器) 原文鏈接https://blog.csdn.net/yang_yulei/article/details/46005845
- 上傳一條得分記錄
- 查看當前排名
- 返回區間 [L,R] 內的排名記錄
總結
以上是生活随笔為你收集整理的P3369 【模板】普通平衡树 Treap树堆学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ME811 刷机
- 下一篇: 《动手学深度学习》组队学习打卡Task5