bzoj 3224: Tyvj 1728 普通平衡树 替罪羊树
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
这题用替罪羊树过的。
替罪羊树, 其实就是一种很暴力的方法。 如果一个节点的左子树节点个数大于它节点个数*alpha或者右子树节点个数大于节点个数*alpha。 那么就将这个节点及它的子树重构。
重构非常暴力, 就是把它变成一个序列, 然后不停的找中点。 相当于将之前的不平衡的二叉树变成了一个完全二叉树。
在删除时, 并不是直接删除, 而是将这个节点打个标记。
代码中, sz是有效节点的个数, 而cover是总结点的个数。 在删除的时候, 如果根节点的sz < alpha*cover。 那么就将整棵树重构。
#include <bits/stdc++.h> using namespace std; #define MAXN 100010 const double alpha = 0.75; struct node { int sz, cover, val; bool exist; node* ch[2]; node() { ch[0] = ch[1] = NULL; exist = false; sz = cover = 0; } node(int val):val(val) { sz = cover = 1; exist = true; ch[0] = ch[1] = NULL; } void pushUp(node* tmp) { sz += tmp->sz; cover += tmp->cover; } bool isBad() { int tmpL = ch[0]?ch[0]->cover:0; int tmpR = ch[1]?ch[1]->cover:0; if(tmpL > cover * alpha + 5 || tmpR > cover * alpha + 5) return true; return false; } }; node* root = NULL; void add(node*& p, int val, node** flag) { if(p == NULL) { p = new node(val); return ; } p->sz++; p->cover++; add(p->ch[val >= p->val], val, flag); if(p->isBad()) { flag = &p; } } void Erase(node* p, int k) { int x = p->ch[0]?p->ch[0]->sz:0; int tmp = x + (int)(p->exist); p->sz--; if(p->exist && tmp == k) { p->exist = false; return ; } if(k <= tmp) { Erase(p->ch[0], k); } else { Erase(p->ch[1], k - tmp); } } void traval(node*& p, vector<node*>& v) { if(p == NULL) return ; traval(p->ch[0], v); if(p->exist) { v.push_back(p); } traval(p->ch[1], v); } void divide(node*& p, vector<node*>& v, int l, int r) { if(l > r) return ; if(p == NULL) p = new node(); int mid = l + r >> 1; p = v[mid]; divide(p->ch[0], v, l, mid - 1); divide(p->ch[1], v, mid + 1, r); if(p->ch[0]) p->pushUp(p->ch[0]); if(p->ch[1]) p->pushUp(p->ch[1]); if(p->exist) p->sz++; p->cover++; } void rebuild(node*& p) { vector <node*> v; traval(p, v); divide(p, v, 0, v.size()-1); } int Rank(node*& p, int x) { if(p == NULL) return 1; int ret = 0; if(p->val >= x) { ret = Rank(p->ch[0], x); } else { int tmp = (p->ch[0])?p->ch[0]->sz:0; ret = tmp + (int)p->exist; ret += Rank(p->ch[1], x); } return ret; } int Kth(node*& p, int k) { if(p == NULL) return 1; int tmp = (p->ch[0])?p->ch[0]->sz:0; if(p->exist && tmp + 1 == k) return p->val; if(tmp >= k) { return Kth(p->ch[0], k); } else { return Kth(p->ch[1], k - tmp - p->exist); } } void Insert(int val) { node** flag = NULL; add(root, val, flag); if(flag != NULL) { rebuild(*flag); } } void Erase(int k) { Erase(root, k); if((double)root->sz < (double)(alpha*root->cover)) rebuild(root); } int main() { int n, sign, x; cin>>n; while(n--) { scanf("%d%d", &sign, &x); switch(sign) { case 1: Insert(x);break; case 2: Erase(root, Rank(root, x));break; case 3: printf("%d\n", Rank(root, x));break; case 4: printf("%d\n", Kth(root, x));break; case 5: printf("%d\n", Kth(root, Rank(root, x) - 1));break; case 6: printf("%d\n", Kth(root, Rank(root, x + 1)));break; } } return 0; }