平衡树专题
1.普通Treap
通过左右旋来维护堆的性质
左右旋是不改变中序遍历的
#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; 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; } int newnode(int val) { int cur = ++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]; } else if(!t[cur].ch[1]) { cur = t[cur].ch[0]; } 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 操作来维护平衡树
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; Node(){ch[0] = ch[1] = siz = val = 0;} }t[MAXN]; int n, Root, poolcur; inline void pushup(int cur) { t[cur].siz = t[lson].siz + t[rson].siz + 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].ch[0] = t[cur].ch[1] = 0; 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); 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)); 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 实现区间操作
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; }
3.替罪羊树
选定一个平衡因子Alpha,一般取值在0.6 ~ 0.7之间,0.75也可以
满足条件 ((cur.siz * Alpha >= lson.siz) and (cur.siz * Alpha >= rson.siz))则视为以cur为根的子树平衡
怎么使一棵树平衡呢
重构!
将以cur为根的子树压成一个序列(中序遍历)
将这个序列暴力重构成一棵平衡树
再把它接回去就好了
并不是能很好的支持删除操作
所以上网找了一个
或是打上删除标记在重构的时候不计入这个元素
一些细节见注释
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #include<cmath> #define lson t[cur].ch[0] #define rson t[cur].ch[1] using namespace std; const int MAXN = 500001, inf = 0x7fffffff; const double Alpha = 0.7; struct Node{ int ch[2], val, fa, siz; Node(){ch[0] = ch[1] = val = siz = fa = 0;} }t[MAXN]; int n, Root, poolcur; int rebin[MAXN], bincur; 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 bool Balance(int cur) { //check balance return (((double)t[cur].siz * Alpha >= t[lson].siz) and ((double)t[cur].siz * Alpha >= t[rson].siz)); } inline void pushup(int cur) { t[cur].siz = t[lson].siz + t[rson].siz + 1; return; } void Recycle(int cur) { //inorder traversal, //store it's No. will optimize the options later. if(lson) Recycle(lson); rebin[++bincur] = cur; if(rson) Recycle(rson); return; } int Build(int l, int r) { //there, 'return 0' is because maybe it used to have sons. if(l > r) return 0; int mid = ((l + r) >> 1), Top = rebin[mid]; t[ t[Top].ch[0] = Build(l, mid - 1) ].fa = Top; t[ t[Top].ch[1] = Build(mid + 1, r) ].fa = Top; pushup(Top); return Top; } void Rebuild(int Top) { //Recycle the numbers, //build the numbers into a BST, //link it with it's old father (two dirctions), //check if Top is the Root. bincur = 0; Recycle(Top); int topfa = t[Top].fa, d = (t[ t[Top].fa ].ch[1] == Top); int cur = Build(1, bincur); t[ t[topfa].ch[d] = cur ].fa = topfa; if(Top == Root) Root = cur; return; } void Insert(int val) { //find a position to insert. //int now = Root, cur = delcur ? delpool[--delcur] : ++poolcur; int now = Root, cur = ++poolcur; t[cur].siz = 1; t[cur].val = val; while(1) { ++t[now].siz; int d = (val >= t[now].val); if(t[now].ch[d]) now = t[now].ch[d]; else { t[ t[now].ch[d] = cur ].fa = now; break; } } int k = 0; for(int i = t[cur].fa; i; i = t[i].fa) if(!Balance(i)) k = i; if(k) Rebuild(k); } 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 getnum(int val) { int cur = Root; while(1) { if(t[cur].val == val) return cur; else cur = t[cur].ch[t[cur].val < val]; } } void Remove(int GG) { int cur = getnum(GG); if(lson and rson) { int now = lson; while(t[now].ch[1]) now = t[now].ch[1]; t[cur].val = t[now].val; cur = now; } int son = (lson) ? lson : rson; int d = (t[ t[cur].fa ].ch[1] == cur); t[ t[ t[cur].fa ].ch[d] = son ].fa = t[cur].fa; for(int i = t[cur].fa; i; i = t[i].fa) --t[i].siz; if(cur == Root) Root = son; return; } int findkth(int cur, int k) { if(!cur) return 0; if(t[lson].siz >= k) return findkth(lson, k); if(t[lson].siz + 1 == k) return t[cur].val; return findkth(rson, k - t[lson].siz - 1); } int getpre(int val) { int cur = Root, ans = -inf; while(cur) { if(t[cur].val < val) ans = max(ans, t[cur].val), cur = t[cur].ch[1]; else cur = t[cur].ch[0]; } return ans; } int getnxt(int val) { int cur = Root, ans = inf; while(cur) { if(t[cur].val > val) ans = min(ans, t[cur].val), cur = t[cur].ch[0]; else cur = t[cur].ch[1]; } return ans; } inline void init() { Root = 1; poolcur = 2; t[0].siz = 0; t[1].val = -inf; t[2].val = inf; t[1].siz = 2; t[2].siz = 1; t[1].ch[1] = 2; t[2].fa = 1; return; } int main() { scanf("%d", &n); int opt, x; init(); for(int i = 1; i <= n; ++i) { scanf("%d%d", &opt, &x); switch(opt) { case 1:{ Insert(x); break; } case 2:{ Remove(x); break; } case 3:{ printf("%d\n", getrnk(Root, x)); break; } case 4:{ printf("%d\n", findkth(Root, x + 1)); break; } case 5:{ printf("%d\n", getpre(x)); break; } case 6:{ printf("%d\n", getnxt(x)); break; } } } return 0; }
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/