平衡树巩固——part1.简单
最近预计做一下各种平衡树的题,这是第一部分,就是一类简单的各种操作,由于实在是太简单了,各种数据结构都可以胜任,我就用treap和sbt分别做了一遍平衡树的三大水题,于是这两种数据结构就比较熟练了。
treap就是普通的二叉排序树多满足了一个堆的性质,相当好写,贴一贴三道题的代码:
1.hnoi2002 turnover(无敌大水题)
#include <stdio.h> #include <stdlib.h> #include <time.h> const int nmax = 32767; int ans, n, tot, root; struct typetreapnode { int chi[2], val, fix; }tap[nmax + 18]; int min(int a, int b) { return a > b ? b : a; } void rotate_treap(int m, int &p)//0 left 1 right { int b = tap[p].chi[m ^ 1]; tap[p].chi[m ^ 1] = tap[b].chi[m]; tap[b].chi[m] = p; if (p == root) root = b; p = b; } void insert_treap(int x, int &p) { if (!p) { p = ++tot; tap[tot].val = x; tap[tot].fix = rand(); } else { int m = (tap[p].val <= x); insert_treap(x, tap[p].chi[m]); if (tap[tap[p].chi[m]].fix < tap[p].fix) rotate_treap(m ^ 1, p); } } void find_treap(int m, int val, int p, int &opt) { if (!p) return; bool k = (tap[p].val >= val); if (!(k ^ m)) find_treap(m, val, tap[p].chi[m ^ 1], opt = p); else find_treap(m, val, tap[p].chi[m], opt); } int main() { srand((unsigned) time(NULL)); scanf("%d%d", &n, &ans); insert_treap(ans, root); root = 1; for (int i = 2, tmp, pre, suc; i <= n; ++i) { scanf("%d", &tmp); pre = suc = 0; if (i == n) i = i + i - i; find_treap(0, tmp, root, pre); find_treap(1, tmp, root, suc); ans += min(pre ? tmp - tap[pre].val : 99999999, suc ? tap[suc].val - tmp : 99999999); insert_treap(tmp, root); } printf("%d", ans); return 0; }
2.hnoi 2004 pet
#include <stdio.h> #include <stdlib.h> #include <time.h> const int nmax = 80000, mo = 1000000; struct typetreapnode { int chi[2], fix, val; }tap[nmax + 18]; int n, now, rnt, ans, root, tot; void rotate(int &p, int m) { int b = tap[p].chi[m ^ 1]; tap[p].chi[m ^ 1] = tap[b].chi[m]; tap[b].chi[m] = p; p = b; } void insert_treap(int &p, int x) { if (!p) { tap[p = ++tot].val = x; tap[p].fix = rand(); } else { int m = (tap[p].val < x); insert_treap(tap[p].chi[m], x); if (tap[p].fix > tap[tap[p].chi[m]].fix) rotate(p, m ^ 1); } } void findround_treap(int p, int x, int m, int &opt) { if (!p) return; int k = (tap[p].val >= x); if (!(k ^ m)) findround_treap(tap[p].chi[m ^ 1], x, m, opt = p); else findround_treap(tap[p].chi[m], x, m, opt); } void delete_treap(int &p, int x) { if (x == tap[p].val) { if (!tap[p].chi[0] || !tap[p].chi[1]) p = tap[p].chi[!tap[p].chi[0]]; else { int m = tap[tap[p].chi[0]].fix < tap[tap[p].chi[1]].fix; rotate(p, m ^ 1); delete_treap(tap[p].chi[m ^ 1], x); } } else delete_treap(tap[p].chi[tap[p].val < x], x); } int better(int a, int b, int x) { if (!a) return b; if (!b) return a; return abs(tap[a].val - x) > abs(tap[b].val - x) ? b : a; } int main() { scanf("%d", &n); srand((unsigned)time(NULL)); for (int i = 1, a, b, pre, suc; i <= n; ++i) { scanf("%d%d", &a, &b); if (!rnt || a == now) insert_treap(root, b), now = a, ++rnt; else { pre = suc = 0; findround_treap(root, b, 0, pre); findround_treap(root, b, 1, suc); pre = better(pre, suc, b); ans = (ans + abs(b - tap[pre].val)) % mo; delete_treap(root, tap[pre].val); --rnt; } } printf("%d", ans); return 0; }
3.noi2004 cashier
#include <stdio.h> #include <stdlib.h> #include <time.h> const int nmax = 100000; struct typetreapnode { int chi[2], val, fix, sze; }tap[nmax + 18]; int n, min, ans, del, root, tot, tmp; char c; void rotate_treap(int &p, int m) //0 left 1 right { int b = tap[p].chi[m ^ 1]; tap[p].chi[m ^ 1] = tap[b].chi[m]; tap[b].chi[m] = p; tap[b].sze = tap[p].sze; tap[p].sze = tap[tap[p].chi[0]].sze + tap[tap[p].chi[1]].sze + 1; p = b; } void insert_treap(int &p, int x) { if (!p) { tap[p = ++tot].val = x; tap[tot].sze = 1; tap[tot].fix = rand(); } else { int m = (tap[p].val < x); ++tap[p].sze; insert_treap(tap[p].chi[m], x); if (tap[tap[p].chi[m]].fix < tap[p].fix) rotate_treap(p, m ^ 1); } } void deletegroup_treap(int &p, int x) { if (!p) return; if (tap[p].val < x) { tap[p].sze = 0; deletegroup_treap(p = tap[p].chi[1], x); } else { deletegroup_treap(tap[p].chi[0], x); tap[p].sze = tap[tap[p].chi[0]].sze + tap[tap[p].chi[1]].sze + 1; } } void findbyrank_treap(int p, int rank, int &pos) { if ((tmp = tap[tap[p].chi[0]].sze + 1) == rank) return (void) (pos = p); if (tmp < rank) findbyrank_treap(tap[p].chi[1], rank - tmp, pos); else findbyrank_treap(tap[p].chi[0], rank, pos); } int main() { srand((unsigned) time(NULL)); scanf("%d%d\n", &n, &min); for (int i = 1, k; i <= n; ++i) { scanf("%c %d\n", &c, &k); if (c == 'I') if (k >= min) insert_treap(root, k - del), ++ans; else ans = ans; else if (c == 'A') del += k;//lowbound = min + del else if (c == 'S') { del -= k; deletegroup_treap(root, min - del); } else if (k <= tap[root].sze) { int pos; findbyrank_treap(root, tap[root].sze - k + 1, pos); printf("%d\n", tap[pos].val + del); } else printf("-1\n"); } printf("%d", ans - tap[root].sze); return 0; }
sbt和treap相当像,只不过把维护堆性质的操作换成了maintain,这个过程其实不是很好懂,每次都要推好久,不过代码只有8行,果断背啊!
1.turnover
#include <stdio.h> #include <stdlib.h> const int nmax = 32767; int n, ans, root, tot, pre, suc; struct typesbt { int sze, val, chi[2]; }sbt[nmax + 18]; void rotate(int &p, int m) //l 0 r 1 { int b = sbt[p].chi[m ^ 1]; sbt[p].chi[m ^ 1] = sbt[b].chi[m]; sbt[b].chi[m] = p; sbt[b].sze = sbt[p].sze; sbt[p].sze = sbt[sbt[p].chi[0]].sze + sbt[sbt[p].chi[1]].sze + 1; p = b; } void maintain(int &p, int m) { if (sbt[sbt[sbt[p].chi[m]].chi[m]].sze > sbt[sbt[p].chi[!m]].sze) rotate(p, !m); else if (sbt[sbt[sbt[p].chi[m]].chi[!m]].sze > sbt[sbt[p].chi[!m]].sze) rotate(sbt[p].chi[m], m), rotate(p, !m); else return; maintain(sbt[p].chi[0], 0); maintain(sbt[p].chi[1], 1); maintain(p, 0); maintain(p, 1); } void insert_sbt(int &p, int x) { if (!p) { sbt[p = ++tot].sze = 1; sbt[p].val = x; } else { ++sbt[p].sze; insert_sbt(sbt[p].chi[sbt[p].val <= x], x); maintain(p, x >= sbt[p].val); } } void searchround_sbt(int p, int x, int m, int &opt) { if (!p) return ; int k = (sbt[p].val >= x); if (!(m ^ k)) searchround_sbt(sbt[p].chi[m ^ 1], x, m, opt = p); else searchround_sbt(sbt[p].chi[m], x, m, opt); } int getmin(int a, int b, int p) { if (!a) return abs(p - sbt[b].val); if (!b) return abs(p - sbt[a].val); return ((a = abs(p - sbt[a].val)) > (b = abs(p - sbt[b].val))) ? b : a; } int main() { scanf("%d", &n); scanf("%d", &ans); insert_sbt(root, ans); for (int i = 2, tmp; i <= n; ++i) { scanf("%d", &tmp); pre = suc = 0; searchround_sbt(root, tmp, 0, pre); searchround_sbt(root, tmp, 1, suc); ans += getmin(pre, suc, tmp); insert_sbt(root, tmp); } printf("%d", ans); return 0; }
2.pet
#include <stdio.h> #include <stdlib.h> const int nmax = 80000, mo = 1000000; struct typesbt { int sze, val, chi[2]; }sbt[nmax + 18]; int n, now, rnt, tot, ans; int root, pre, suc; void rotate(int &p, int m) { int b = sbt[p].chi[!m]; sbt[p].chi[!m] = sbt[b].chi[m]; sbt[b].chi[m] = p; sbt[b].sze = sbt[p].sze; sbt[p].sze = sbt[sbt[p].chi[0]].sze + sbt[sbt[p].chi[1]].sze + 1; p = b; } void maintain(int &p, int m) { if (sbt[sbt[sbt[p].chi[m]].chi[m]].sze > sbt[sbt[p].chi[!m]].sze) rotate(p, !m); else if (sbt[sbt[sbt[p].chi[m]].chi[!m]].sze > sbt[sbt[p].chi[!m]].sze) rotate(sbt[p].chi[m], m), rotate(p, !m); else return; maintain(sbt[p].chi[0], 0); maintain(sbt[p].chi[1], 1); maintain(p, 0); maintain(p, 1); } void insert_sbt(int &p, int x) { if (!p) { sbt[p = ++tot].sze = 1; sbt[tot].val = x; } else { ++sbt[p].sze; insert_sbt(sbt[p].chi[sbt[p].val <= x], x); maintain(p, sbt[p].val <= x); } } void searchround_sbt(int p, int m, int x, int &opt) { if (!p) return; int k = (sbt[p].val >= x); if (!(m ^ k)) searchround_sbt(sbt[p].chi[!m], m, x, opt = p); else searchround_sbt(sbt[p].chi[m], m, x, opt); } int deletenode_sbt(int &p, int x) { --sbt[p].sze; if (x == sbt[p].val || !sbt[p].chi[x > sbt[p].val]) { int rnt = sbt[p].val; if (!sbt[p].chi[0] || !sbt[p].chi[1]) p = sbt[p].chi[!sbt[p].chi[0]]; else sbt[p].val = deletenode_sbt(sbt[p].chi[0], x + 1); return rnt; } else return deletenode_sbt(sbt[p].chi[x > sbt[p].val], x); } int chkmin(int &a, int b, int c) { if (!a || abs(sbt[a].val - c) > abs(sbt[b].val - c)) a = b; return abs(sbt[a].val - c); } int main() { scanf("%d", &n); for (int i = 1, a, b; i <= n; ++i) { scanf("%d%d", &a, &b); if (!rnt || now == a) insert_sbt(root, b), now = a, ++rnt; else { suc = pre = 0; searchround_sbt(root, 0, b, pre); searchround_sbt(root, 1, b, suc); ans = (ans + chkmin(pre, suc, b)) % mo; --rnt; deletenode_sbt(root, sbt[pre].val); } } printf("%d", ans); return 0; }
3.cashier
#include <stdio.h> const int nmax = 100000; struct typesbt { int sze, chi[2], val; }sbt[nmax + 18]; int n, ans, del, min, tot, num, root; char c; void rotate(int &p, int m) { int b = sbt[p].chi[!m]; sbt[p].chi[!m] = sbt[b].chi[m]; sbt[b].chi[m] = p; sbt[b].sze = sbt[p].sze; sbt[p].sze = sbt[sbt[p].chi[0]].sze + sbt[sbt[p].chi[1]].sze + 1; p = b; } void maintain(int &p, int m) { if (sbt[sbt[sbt[p].chi[m]].chi[m]].sze > sbt[sbt[p].chi[!m]].sze) rotate(p, !m); else if (sbt[sbt[sbt[p].chi[m]].chi[!m]].sze > sbt[sbt[p].chi[!m]].sze) rotate(sbt[p].chi[m], m), rotate(p, !m); else return; maintain(sbt[p].chi[0], 0); maintain(sbt[p].chi[1], 1); maintain(p, 0); maintain(p, 1); } void insert_sbt(int &p, int x) { if (!p) { sbt[p = ++tot].sze = 1; sbt[p].val = x; } else { ++sbt[p].sze; insert_sbt(sbt[p].chi[sbt[p].val <= x], x); maintain(p, sbt[p].val <= x); } } void deletegroup_sbt(int &p, int x) { if (!p) return; if (sbt[p].val < x) { sbt[p].sze = 0; deletegroup_sbt(p = sbt[p].chi[1], x); } else { deletegroup_sbt(sbt[p].chi[0], x); sbt[p].sze = sbt[sbt[p].chi[0]].sze + sbt[sbt[p].chi[1]].sze + 1; } } int getbyrank_sbt(int p, int k) { int tmp = sbt[sbt[p].chi[0]].sze + 1; if (tmp == k) return p; if (tmp < k) return getbyrank_sbt(sbt[p].chi[1], k - tmp); else return getbyrank_sbt(sbt[p].chi[0], k); } int main() { scanf("%d%d", &n, &min); for (int i = 1, tmp; i <= n; ++i) { scanf("\n%c %d", &c, &tmp); if (c == 'I') if (tmp >= min) insert_sbt(root, tmp - del), ++num; else ; else if (c == 'A') del += tmp; else if (c == 'S') { del -= tmp; deletegroup_sbt(root, min - del); } else printf("%d\n", tmp <= sbt[root].sze ? sbt[getbyrank_sbt(root, sbt[root].sze - tmp + 1)].val + del : -1); } printf("%d", num - sbt[root].sze); return 0; }
总之,上面这些水题多多少少耗了我两个晚自习,下面就要向序列之神,维护之神——splay进发了,去年的时候搞了一下,现在看来发现丑得无可比拟,一定要重写一个比较漂亮的单旋版本!