Treap与fhq_Treap模板(支持内存回收)
1.普通Treap
通過左右旋來維護堆的性質
左右旋是不改變中序遍歷的
這里有幾點要注意一下:
(1).由于寫了內存回收,在 newnode 的時候要 memset 一下這個節點,因為可能被用過
(2).在刪除時要這么寫 : 如果只有小于等于1個兒子就直接刪
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #include<cmath> #include<ctime> using namespace std;const int MAXN = 100100, inf = 0x7fffffff;struct Node{int ch[2];int val, prio;int cnt, siz; // Node(){ch[0] = ch[1] = val = cnt = siz = 0;} }t[MAXN];int n; int root, pool_cur, delpool[MAXN], delcur;void init(); int newnode(int val); void delnode(int cur); void pushup(int cur); void rotate(int &cur, int d); void Insert(int &cur, int val); void Remove(int &cur, int val); int getpre(int val); int getnxt(int val); int getrankbyval(int cur, int val); int getvalbyrank(int cur, int rank);int main() {srand(time(NULL));scanf("%d", &n);int opt, x;init();for(int i = 1; i <= n; ++i) {scanf("%d%d", &opt, &x);switch(opt) {case 1:{Insert(root, x);break;}case 2:{Remove(root, x);break;}case 3:{printf("%d\n", getrankbyval(root, x) - 1);break;}case 4:{printf("%d\n", getvalbyrank(root, x + 1));break;}case 5:{printf("%d\n", getpre(x));break;}case 6:{printf("%d\n", getnxt(x));break;}}}return 0; }void init() {newnode(-inf);newnode(inf);root = 1;t[1].ch[1] = 2;pushup(root);return; }inline void delnode(int cur) {delpool[++delcur] = cur;return; }int newnode(int val) {int cur = (delcur ? delpool[delcur--] : ++pool_cur);memset(t + cur, 0, sizeof(Node));t[cur].siz = t[cur].cnt = 1;t[cur].prio = rand();t[cur].val = val;return cur; }void pushup(int cur) {t[cur].siz = t[t[cur].ch[0]].siz + t[t[cur].ch[1]].siz + t[cur].cnt;return; }void rotate(int &cur, int d) {int u = t[cur].ch[d];t[cur].ch[d] = t[u].ch[d^1];t[u].ch[d^1] = cur;t[u].siz = t[cur].siz;pushup(cur);cur = u;return; }void Insert(int &cur, int val) {if(cur == 0) {cur = newnode(val);return;}if(t[cur].val == val) {++t[cur].cnt;pushup(cur);return;}int d = t[cur].val < val;Insert(t[cur].ch[d], val);pushup(cur);if(t[t[cur].ch[d]].prio < t[cur].prio) rotate(cur, d);return; }void Remove(int &cur, int val) {if(!cur) return;if(t[cur].val == val) {int o = cur;if(t[cur].cnt > 1) {--t[cur].cnt;} else {if(!t[cur].ch[0]) {cur = t[cur].ch[1];delnode(o);} else if(!t[cur].ch[1]) {cur = t[cur].ch[0];delnode(o);} else {int d = t[t[cur].ch[0]].prio < t[t[cur].ch[1]].prio;rotate(cur, d ^ 1);Remove(t[cur].ch[d], val);}}pushup(cur);} else {int d = t[cur].val < val;Remove(t[cur].ch[d], val);}pushup(cur);return; }int getpre(int val) {int ans = 1;int cur = root;while(cur) {if(val == t[cur].val) {if(t[cur].ch[0] > 0) {cur = t[cur].ch[0];while(t[cur].ch[1] > 0) cur = t[cur].ch[1];ans = cur;}break;}if(t[cur].val < val and t[cur].val > t[ans].val) ans = cur;cur = t[cur].val > val ? t[cur].ch[0] : t[cur].ch[1];}return t[ans].val; }int getnxt(int val) {int ans = 2;int cur = root;while(cur) {if(val == t[cur].val) {if(t[cur].ch[1] > 0) {cur = t[cur].ch[1];while(t[cur].ch[0] > 0) cur = t[cur].ch[0];ans = cur;}break;}if(t[cur].val > val and t[cur].val < t[ans].val) ans = cur;cur = t[cur].val > val ? t[cur].ch[0] : t[cur].ch[1];}return t[ans].val; }int getrankbyval(int cur, int val) {if(!cur) return 0;if(val == t[cur].val) return t[t[cur].ch[0]].siz + 1;return t[cur].val > val ? getrankbyval(t[cur].ch[0], val) : (getrankbyval(t[cur].ch[1], val) + t[t[cur].ch[0]].siz + t[cur].cnt); }int getvalbyrank(int cur, int rank) {if(!cur) return 0;if(t[t[cur].ch[0]].siz >= rank) return getvalbyrank(t[cur].ch[0], rank);if(t[t[cur].ch[0]].siz + t[cur].cnt >= rank) return t[cur].val;return getvalbyrank(t[cur].ch[1], rank - t[t[cur].ch[0]].siz - t[cur].cnt); }?
2.fhq_Treap (非旋 Treap )
通過 Split 和 Merge 操作來維護平衡樹
(1) 用 fhq_Treap 實現普通 Treap 支持的操作
Split :
把以 cur 為根的樹的前 k 個元素分離開
返回值為分離后的兩根
pair<int, int> Split(int cur, int k) {if(!cur or !k) return make_pair(0, cur);pair<int, int> res;if(t[lson].siz >= k) {res = Split(lson, k);lson = res.second;pushup(cur);res.second = cur;} else {res = Split(rson, k - t[lson].siz - 1);rson = res.first;pushup(cur);res.first = cur;}return res; }進入右子樹,則一定選當前節點,將它作為分離后左邊那棵樹的根
進入左子樹,則一定不選當前節點,將它作為分離后的右邊那棵樹的根
Merge :?
像可并堆那樣進行合并
這樣來滿足堆性質
int Merge(int x, int y) {if(!x) return y; if(!y) return x;if(t[x].prio < t[y].prio) {t[x].ch[1] = Merge(t[x].ch[1], y);pushup(x);return x;} else {t[y].ch[0] = Merge(x, t[y].ch[0]);pushup(y);return y;} }這里寫一種比較清奇的 getrank , 返回所有小于 val 的元素個數
很好用的
int getrnk(int cur, int val) {if(!cur) return 0;return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1); }加哨兵總是掛,最后就沒加 = =
好像也不用加
?
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #include<cmath> #include<ctime> #define lson t[cur].ch[0] #define rson t[cur].ch[1] using namespace std;const int MAXN = 100001;struct Node{int ch[2], siz, prio, val; }t[MAXN]; int n, Root, poolcur; int delpool[MAXN], delcur;inline void pushup(int cur) {t[cur].siz = t[lson].siz + t[rson].siz + 1;return; } inline void delnode(int cur) {delpool[++delcur] = cur;return; } inline int newnode(int val) {register int cur = (delcur ? delpool[delcur--] : ++poolcur);memset(t + cur, 0, sizeof(Node));t[cur].val = val;t[cur].siz = 1;t[cur].prio = rand();return cur; } pair<int, int> Split(int cur, int k) {if(!cur or !k) return make_pair(0, cur);pair<int, int> res;if(t[lson].siz >= k) {res = Split(lson, k);lson = res.second;pushup(cur);res.second = cur;} else {res = Split(rson, k - t[lson].siz - 1);rson = res.first;pushup(cur);res.first = cur;}return res; } int Merge(int x, int y) {if(!x) return y; if(!y) return x;if(t[x].prio < t[y].prio) {t[x].ch[1] = Merge(t[x].ch[1], y);pushup(x);return x;} else {t[y].ch[0] = Merge(x, t[y].ch[0]);pushup(y);return y;} } int getrnk(int cur, int val) {if(!cur) return 0;return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1); } int findkth(int k) {pair<int, int> x = Split(Root, k - 1);pair<int, int> y = Split(x.second, 1);int ans = t[y.first].val;Root = Merge(Merge(x.first, y.first), y.second);return ans; } int getpre(int val) {register int k = getrnk(Root, val);return findkth(k); } int getnxt(int val) {register int k = getrnk(Root, val + 1);return findkth(k + 1); } void Insert(int val) {pair<int, int> x = Split(Root, getrnk(Root, val));Root = Merge(Merge(x.first, newnode(val)), x.second);return; } void Remove(int val) {register int k = getrnk(Root, val);pair<int, int> x = Split(Root, k);pair<int, int> y = Split(x.second, 1);delnode(y.first);Root = Merge(x.first, y.second);return; } inline int rd() {register int x = 0;register char c = getchar();register bool f = false;while(!isdigit(c)) {if(c == '-') f = true;c = getchar();}while(isdigit(c)) {x = x * 10 + c - 48;c = getchar();}return f ? -x : x; }int main() {srand(time(NULL));memset(t, 0, sizeof(Node)); n = rd();int opt, x;while(n--) {opt = rd(); x = rd();switch(opt) {case 1: {Insert(x);break;}case 2: {Remove(x);break;}case 3: {printf("%d\n", getrnk(Root, x) + 1);break;}case 4: {printf("%d\n", findkth(x));break;}case 5: {printf("%d\n", getpre(x));break;}case 6: {printf("%d\n", getnxt(x));break;}}}return 0; }?
?
(2) 用 fhq_Treap 實現 Splay 支持的區間操作
fhq_Treap是支持拼接子樹的,所以也能夠很好的支持區間操作
要對區間 [ l, r ] 進行操作,就先把前 r 個元素 Split 出來,再將前 l - 1 個元素 Split 出來,這樣就得到了區間 [ l, r ] 的一棵Treap
之后進行想要的操作即可
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #include<cmath> #include<ctime> #define lson t[cur].ch[0] #define rson t[cur].ch[1] using namespace std;const int MAXN = 100001;struct Node{int ch[2], siz, val, prio;bool rvrs;Node(){ch[0] = ch[1] = siz = val = 0; rvrs = false;} }t[MAXN]; int n, m, Root, poolcur;inline int rd() {register int x = 0;register char c = getchar();register bool f = false;while(!isdigit(c)) {if(c == '-') f = true;c = getchar();}while(isdigit(c)) {x = x * 10 + c - 48;c = getchar();}return f ? -x : x; } inline void pushup(int cur) {t[cur].siz = t[lson].siz + t[rson].siz + 1;return; } inline void pushdown(int cur) {if(cur && t[cur].rvrs) {t[cur].rvrs = false;swap(lson, rson);t[lson].rvrs ^= 1;t[rson].rvrs ^= 1;}return; } inline int newnode(int val) {register int cur = ++poolcur;t[cur].val = val;t[cur].siz = 1;t[cur].prio = rand();t[cur].rvrs = false;return cur; } pair<int, int> Split(int cur, int k) {if(!cur or !k) return make_pair(0, cur);pushdown(cur); pair<int, int> res;if(t[lson].siz >= k) {res = Split(lson, k);lson = res.second;pushup(cur);res.second = cur;} else {res = Split(rson, k - t[lson].siz - 1);rson = res.first;pushup(cur);res.first = cur;}return res; } int Merge(int x, int y) {pushdown(x); pushdown(y);if(!x) return y; if(!y) return x;if(t[x].prio < t[y].prio) {t[x].ch[1] = Merge(t[x].ch[1], y);pushup(x);return x;} else {t[y].ch[0] = Merge(x, t[y].ch[0]);pushup(y);return y;} } int getrnk(int cur, int val) {if(!cur) return 0;return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1); } void Insert(int val) {pair<int, int> x = Split(Root, getrnk(Root, val));Root = Merge(Merge(x.first, newnode(val)), x.second);return; } void Reverse(int l, int r) {pair<int, int> x = Split(Root, r);pair<int, int> y = Split(x.first, l - 1);t[y.second].rvrs ^= 1;Root = Merge(Merge(y.first, y.second), x.second);return; } void Recycle(int cur) {if(!cur) return;pushdown(cur);if(lson) Recycle(lson);if(t[cur].val > 0 and t[cur].val <= n) printf("%d ", t[cur].val);if(rson) Recycle(rson);return; } inline void init() {t[1].val = t[1].siz = 1;Root = 1; poolcur = 1;t[1].prio = rand();return; }int main() {srand(time(NULL));n = rd(); m = rd();init();for(int i = 2; i <= n; ++i) Insert(i);int l, r;while(m--) {l = rd(); r = rd();Reverse(l, r);}Recycle(Root);putchar('\n');return 0; }Update:?
在寫 bzoj1500 時有些奇怪的疑惑: Split 出來之后再 Merge 回去還是原來的樹嗎?區間反轉的 tag 給當前根打上之后再接回去不會 GG 嗎?
顯然,它還是原來的樹,reverse 之后也并不會 GG . 當原來的 x.first 回到它應該的位置后,x.second 整棵子樹靠近 x.first 一側的 tag 也已經下放至更深一層了
?
轉載于:https://www.cnblogs.com/xcysblog/p/9064774.html
總結
以上是生活随笔為你收集整理的Treap与fhq_Treap模板(支持内存回收)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 域名转IP(可包含http
- 下一篇: poj3279 反转 挑战程序设计竞赛