替罪羊树模板
LOJ #104.普通平衡树
1 #include <bits/stdc++.h> 2 3 const double alpha = 0.75; 4 const int N = 1e5 + 233; 5 int n; 6 7 class ScapegoatTree { 8 private: 9 int cnt, tp; 10 struct Node { 11 Node *ch[2]; 12 int val, siz, exist, cnt; 13 void pushup() {//上传 14 siz = ch[0]->siz + ch[1]->siz + exist; 15 cnt = ch[0]->cnt + ch[1]->cnt + 1; 16 } 17 bool is_bad() {//判断是否失衡 18 return ch[0]->cnt > cnt * alpha + 5 || ch[1]->cnt > cnt * alpha + 5; 19 } 20 } *null, node[N], *root, *stk[N]; 21 Node *newnode(int val) {//新增一个节点 22 Node *ret = node + (++cnt); 23 ret->ch[0] = ret->ch[1] = null; 24 ret->val = val; 25 ret->siz = ret->cnt = ret->exist = 1; 26 return ret; 27 } 28 void dfs(Node *p) {//把需要重构的点扔进栈里 29 if (p == null) return; 30 dfs(p->ch[0]); 31 if (p->exist) stk[++tp] = p; 32 dfs(p->ch[1]); 33 } 34 Node *divide(int l, int r) {//重构树,这样可以保证重构后的树中序遍历与原树相同 35 if (r < l) return null; 36 int mid = (l + r) >> 1; 37 Node *ret = stk[mid]; 38 ret->ch[0] = divide(l, mid - 1); 39 ret->ch[1] = divide(mid + 1, r); 40 ret->pushup(); 41 return ret; 42 } 43 void rebuild(Node *&p) { 44 tp = 0, dfs(p); 45 p = divide(1, tp); 46 } 47 Node **_insert(Node *&p, int val) {//插入函数,同时返回一个指向最上坏点的指针 48 if (p == null) { 49 p = newnode(val); 50 return &null; 51 } 52 if (p->val == val) { 53 ++p->exist, p->pushup(); 54 return &null; 55 } 56 Node **ret; 57 ++p->siz, ++p->cnt; 58 if (val <= p->val) ret = _insert(p->ch[0], val); 59 else ret = _insert(p->ch[1], val); 60 if (p->is_bad()) return &p; 61 p->cnt -= (*ret)->cnt - (*ret)->siz; 62 return ret; 63 } 64 void _delete(Node *p, int val) { 65 if (p->val == val) { 66 --p->exist, p->pushup(); 67 return; 68 } 69 if (p->val < val) _delete(p->ch[1], val); 70 else _delete(p->ch[0], val); 71 p->pushup(); 72 } 73 public: 74 ScapegoatTree() { 75 root = null = node; 76 null->ch[0] = null->ch[1] = null; 77 null->siz = null->cnt = null->val = null->exist = 0; 78 } 79 void insert(int val) { 80 Node **p = _insert(root, val); 81 if (p != &null) rebuild(*p); 82 } 83 void del(int val) { 84 _delete(root, val); 85 if (root->cnt * alpha > root->siz + 5) rebuild(root); 86 } 87 int get_rank(int val) { 88 int ret = 0; 89 Node *p = root; 90 while (p != null) { 91 if (p->val == val) return (ret + p->ch[0]->siz); 92 if (p->val < val) { 93 ret += p->ch[0]->siz + p->exist; 94 p = p->ch[1]; 95 } else p = p->ch[0]; 96 } 97 return ret; 98 } 99 int get_kth(int k) { 100 Node *p = root; 101 while (1) { 102 if (p->ch[0]->siz >= k) p = p->ch[0]; 103 else if (p->ch[0]->siz + p->exist >= k) return p->val; 104 else k -= p->ch[0]->siz + p->exist, p = p->ch[1]; 105 } 106 } 107 }; 108 109 signed main() { 110 static ScapegoatTree *Kamome = new ScapegoatTree(); 111 scanf("%d", &n); 112 for (int i = 1, op, x; i <= n; i++) { 113 scanf("%d%d", &op, &x); 114 switch (op) { 115 case 1: 116 Kamome->insert(x); 117 break; 118 case 2: 119 Kamome->del(x); 120 break; 121 case 3: 122 printf("%d\n", Kamome->get_rank(x) + 1); 123 break; 124 case 4: 125 printf("%d\n", Kamome->get_kth(x)); 126 break; 127 case 5: 128 printf("%d\n", Kamome->get_kth(Kamome->get_rank(x))); 129 break; 130 case 6: 131 printf("%d\n", Kamome->get_kth(Kamome->get_rank(x + 1) + 1)); 132 break; 133 } 134 } 135 return 0; 136 }
~~OOP赛高~~
替罪羊树的主要思想:当一个子树不平衡就拍扁重建。虽然很暴力,但复杂度很科学。
下面开始说注意点:
1.null节点的加入:为了防止访问到空指针之类的需要大力分类讨论的谔谔玩意
2._insert为什么是二维指针?:如果直接把二维指针都改成一维,我们可以得到一个指向坏点的指针p,但在它基础上重构是没有意义的。我们真正想要得到的不是坏点,是指向坏点的那个指针。使用二维指针可以保证我们找到指向坏点的指针。
3.我们乘平衡因子时加的那玩意是干啥的?:防止节点较少时频繁重构,影响时间复杂度。
$ \Theta \omega \Theta $