替罪羊树模板

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.我们乘平衡因子时加的那玩意是干啥的?:防止节点较少时频繁重构,影响时间复杂度。

posted @ 2019-08-04 20:44  Gekoo  阅读(328)  评论(4编辑  收藏  举报