题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
插入x数
删除x数(若有多个相同的数,因只删除一个)
查询x数的排名(若有多个相同的数,因输出最小的排名)
查询排名为x的数
求x的前驱(前驱定义为小于x,且最大的数)
- 求x的后继(后继定义为大于x,且最小的数)
输入输出格式
输入格式:
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
输出格式:
对于操作3,4,5,6每行输出一个数,表示对应答案
输入输出样例
输入样例#1:
10 1 106465 4 1 1 317721 1 460929 1 644985 1 84185 1 89851 6 81968 1 492737 5 493598
输出样例#1:
106465 84185 492737
一道平衡树裸题。涉及很多常用的平衡树操作。但是刚刚接触平衡树,于是就调了一个很难看的Splay给AC了
基本思想:由于有重复数字,这里记一个cnt来存放节点中数的个数。
删除操作:将节点放在前驱节点和后继结点之间然后从后继节点断开(PS这里有一个小技巧,在空平衡树中插入inf和-inf就可以免去一堆特判)
查询排名:用一个push_up维护每个节点的size——子树大小。将节点旋转到根,左子树的size即比该数小的数的个数(注意:子树大小需要加上当前节点的cnt)
查询排名为X的数:从根节点出发,如果当前X在【左子树的size+1】到【左子树的size+cnt】之间,则找到并返回。否则分两种情况向下;
1:当前X>左子树size那么X-=(左子树size+当前cnt),向右继续向下;
2:否则向左继续向下;
那么,最后停下的点必定是原排名为X的节点;
求前驱后继:将当前节点旋转到根节点,从左节点开始一直向右找,一直到当前节点没有右儿子,当前节点为前驱,后继同理。
贴一段不太美观的代码
#include<cstdio> using namespace std; const int MAXN=1e5+100; namespace Splay { struct node { int cnt,size,num,ch[2],fa; void init() {cnt=size=num=ch[0]=ch[1]=fa=0;} node() {init();} void give(int rn,int rf,int rc,int rs) {num=rn;fa=rf;cnt=rc;size=rs;} }tree[MAXN]; int cnt=0,root=0; void push_up(int rt){ if(rt==0) return;//2?pushup0?úμ? int l=tree[rt].ch[0]; int r=tree[rt].ch[1]; tree[rt].size=tree[l].size+tree[r].size+tree[rt].cnt; } void rotate(int rt){ int p=tree[rt].fa; int a=tree[p].fa; int l=tree[p].ch[1]==rt; int r=l^1; if(a) if(tree[a].ch[0]==p) tree[a].ch[0]=rt; else tree[a].ch[1]=rt; tree[rt].fa=a,tree[p].fa=rt,tree[tree[rt].ch[r]].fa=tree[rt].ch[r]?p:0; tree[p].ch[l]=tree[rt].ch[r],tree[rt].ch[r]=p; push_up(p); push_up(rt); } void splay(int now,int y){ int des=tree[y].fa; while(des!=tree[now].fa){ int p=tree[now].fa; int a=tree[p].fa; if(p!=y) if((tree[a].ch[0]==p)^(tree[p].ch[0]==now)) rotate(now); else rotate(p); rotate(now); } if(y==root) root=now;//??μ??üD??ù?úμ? } void insert(int & now,int x,int last){//?éò??üD??ù?úμ? if(now==0){now=++cnt;tree[cnt].give(x,last,1,1); tree[last].ch[tree[last].num<x]=now;splay(now,root);return;} if(tree[now].num==x){tree[now].cnt++;tree[now].size++;splay(now,root);return;} insert(tree[now].ch[tree[now].num<x],x,now); //push_up(now); } int find(int num){ int now=root; while(tree[now].num!=num && now) now=tree[now].ch[tree[now].num<num]; if(!now) return -1; return now; } int qnode(int now,bool type){ //type: 0:?°?y 1:oó?ì splay(now,root); int p=tree[now].ch[type]; if(!p) return -1; while(tree[p].ch[type^1]) p=tree[p].ch[type^1]; return p; } int qrank(int num){ int now=find(num); splay(now,root); return tree[tree[now].ch[0]].size+1; } int qnum(int x){ int now=root; while(x>tree[tree[now].ch[0]].size+tree[now].cnt || x<tree[tree[now].ch[0]].size+1){ int l=tree[now].ch[0]; int r=tree[now].ch[1]; if(tree[tree[now].ch[0]].size+tree[now].cnt<x) x-=(tree[tree[now].ch[0]].size+tree[now].cnt),now=r; else now=l; } return tree[now].num; } void del(int now){ tree[now].cnt--,tree[now].size--; int p=tree[now].fa; if(!tree[now].cnt){ int pr=qnode(now,0); int su=qnode(now,1); splay(pr,root);splay(su,tree[pr].ch[1]); tree[su].ch[0]=0; p=tree[now].fa; tree[now].init(); } while(p){ push_up(p); p=tree[p].fa; } } int qrn(int num,bool type){ insert(root,num,0); int ans=qnode(root,type); del(root); return tree[ans].num; } } int main(){ using namespace Splay; int t; scanf("%d",&t); insert(root,2147483647,0); insert(root,-2147483647,0); while(t--){ int opt,x; scanf("%d%d",&opt,&x); if(opt==1) insert(root,x,0); if(opt==2) del(find(x)); if(opt==3) printf("%d\n",qrank(x) - 1); if(opt==4) printf("%d\n",qnum(x + 1)); if(opt==5) printf("%d\n",qrn(x,0)); if(opt==6) printf("%d\n",qrn(x,1)); } return 0; }
2017-12-23
学习了其他类型的平衡树,发现Splay常数非常大。这里给出Treap和替罪羊树的代码。
需要特别注意的是Treap的删除操作,很容易忘了特判根节点、在有多个权值时不删除节点。另外替罪羊树的删除是假删除(类似KDtree),对功能函数有一定的影响,不好写前驱后继,可以用查询第K大和查询X的排名来代(tou)替(lan)。
Treap:
#include<cstdio> #include<cstdlib> #include<ctime> #include<algorithm> using namespace std; const int maxn = 1e5 + 5; const int inf = 0x7fffffff; int Rand() {return rand() * 666 + rand();} namespace Treap { const int L = 0, R = 1; struct node { int ch[2], num, size, cnt, fa, key; }tree[maxn]; int cnt, root; void init() {srand(time(0)), tree[0].key = inf;} void push_up(int rt) { tree[rt].size = tree[tree[rt].ch[L]].size + tree[tree[rt].ch[R]].size + tree[rt].cnt; } void rotate(int rt) { int p = tree[rt].fa, a = tree[p].fa; int l = tree[p].ch[R] == rt, r = l ^ 1;//用全部是左儿子来想 if(a) tree[a].ch[tree[a].ch[R] == p] = rt; tree[rt].fa = a, tree[p].fa = rt, tree[tree[rt].ch[r]].fa = p; tree[p].ch[l] = tree[rt].ch[r], tree[rt].ch[r] = p; push_up(p), push_up(rt); } void adjust(int rt) { while(tree[rt].key < tree[tree[rt].fa].key) rotate(rt); if(!tree[rt].fa) root = rt; } void insert(int & rt, int x, int last) { if(!rt) rt = ++cnt, tree[rt] = (node) {0, 0, x, 1, 1, last, Rand()}; else { if(tree[rt].num < x) insert(tree[rt].ch[R], x, rt); else if(tree[rt].num > x) insert(tree[rt].ch[L], x, rt); else ++tree[rt].cnt; push_up(rt); } } void Ins(int num) {insert(root, num, 0), adjust(cnt);} void Up(int now) {while(now) push_up(now), now = tree[now].fa;} int find(int num) { int now = root; for(; tree[now].num != num; now = tree[now].ch[tree[now].num < num]); return now; } void del(int num) { int now = find(num); if(tree[now].cnt > 1) --tree[now].cnt, Up(now); else { int ls = tree[now].ch[L], rs = tree[now].ch[R]; if(now == root) { if(!(ls * rs)) root = rs ? rs : ls; else root = tree[ls].key < tree[rs].key ? rs : ls; } for(; ls | rs; ls = tree[now].ch[L], rs = tree[now].ch[R]) { if(!(ls * rs)) rotate(rs ? rs : ls); else rotate(tree[ls].key < tree[rs].key ? rs : ls); } int p = tree[now].fa; tree[p].ch[tree[p].ch[R] == now] = 0, Up(p); } } int qrank(int x, int & c) { int now = root, num = tree[now].num, ans = 0; for(; now; num = tree[now].num) { if(num < x) ans += tree[tree[now].ch[L]].size + tree[now].cnt, now = tree[now].ch[R]; else if(num > x) now = tree[now].ch[L]; else return c = tree[now].cnt, ans + tree[tree[now].ch[L]].size; } return c = tree[now].cnt, ans; } int qnum(int rank) { int tl = tree[root].ch[L], now = root; for(;; tl = tree[now].ch[L]) { if(tree[tl].size >= rank) now = tl; else if(tree[tl].size + tree[now].cnt < rank) rank -= tree[tl].size + tree[now].cnt, now = tree[now].ch[R]; else return tree[now].num; } } } int n, opt, a, b, c, tot; int main() { using namespace Treap; init(); scanf("%d", &n); while(n--) { scanf("%d%d", &opt, &a); if(opt == 1) Ins(a); else if(opt == 2) del(a); else if(opt == 3) printf("%d\n", qrank(a, c) + 1); else if(opt == 4) printf("%d\n", qnum(a)); else if(opt == 5) printf("%d\n", qnum(qrank(a, c))); else printf("%d\n", qnum(qrank(a, c) + c + 1)); } return 0; } /* *注:可以用下面两个函数实现前驱和后继的查找,节省常数。 int pre(int rt, int num) { if(!rt) return -inf; if(tree[rt].num < num) return max(tree[rt].num, pre(tree[rt].ch[R], num)); else return pre(tree[rt].ch[L], num); } int suc(int rt, int num) { if(!rt) return inf; if(tree[rt].num > num) return min(tree[rt].num, suc(tree[rt].ch[L], num)); else return suc(tree[rt].ch[R], num); }*/
替罪羊树:
#include<cstdio> #include<queue> using namespace std; const int maxn = 1e5 + 5, inf = 0x7fffffff; const double per = 0.75; namespace SBT { const int L = 0, R = 1; struct node { int ch[2], size, cnt, num; }tree[maxn]; int cnt, root = 1; int num[maxn], tot[maxn], len; queue<int > q; int New() { int ret = cnt + 1; if(q.empty()) return ++cnt, ret; else ret = q.front(); return q.pop(), ret; } void push_up(int rt) { int tl = tree[rt].ch[L], tr = tree[rt].ch[R]; tree[rt].size = tree[tl].size + tree[tr].size + tree[rt].cnt; } int insert(int & rt, int & fa, const int &x, const int &last) { if(!rt) { rt = ++cnt, tree[rt] = (node) {0, 0, 1, 1, x}; return -1; } int ret = -1, *tr = &tree[rt].ch[R], *tl = &tree[rt].ch[L]; if(tree[rt].num < x) ret = insert(*tr, fa, x, rt); else if(tree[rt].num > x) ret = insert(*tl, fa, x, rt); else ++tree[rt].cnt; push_up(rt); if(tree[*tl].size * 1.0 > tree[rt].size * per) return fa = last, rt; if(tree[*tr].size * 1.0 > tree[rt].size * per) return fa = last, rt; return ret; } void Gettree(int rt) { if(!rt) return; Gettree(tree[rt].ch[L]); num[++len] = tree[rt].num, tot[len] = tree[rt].cnt; Gettree(tree[rt].ch[R]); q.push(rt), tree[rt] = (node){0}; } void Build(int l, int r, int & rt) { rt = New(); int mid = l + r >> 1; tree[rt].num = num[mid]; tree[rt].size = tree[rt].cnt = tot[mid]; if(l < mid) Build(l, mid - 1, tree[rt].ch[L]); if(r > mid) Build(mid + 1, r, tree[rt].ch[R]); push_up(rt); } void ReBuild(int rt, int fa) { len = 0, Gettree(rt); int Nrt = rt; Build(1, len, Nrt); tree[fa].ch[tree[fa].ch[R] == rt] = Nrt; if(rt == root) root = Nrt; } int qnum(int rank) { int tl = tree[root].ch[L], now; for(now = root; now; tl = tree[now].ch[L]) { if(tree[tl].size >= rank) now = tl; else if(tree[tl].size + tree[now].cnt < rank) rank -= tree[tl].size + tree[now].cnt, now = tree[now].ch[R]; else return tree[now].num; } return tree[now].num; } int qrank(int x, int & c) { int num = tree[root].num, ans = 0, now; c = 0; for(now = root; now; num = tree[now].num) { if(tree[now].num < x) { ans += tree[tree[now].ch[L]].size; ans += tree[now].cnt; now = tree[now].ch[R]; }else if(tree[now].num > x) now = tree[now].ch[L]; else { ans += tree[tree[now].ch[L]].size; c = tree[now].cnt; break; } } return ans; } void del(int rt, int x) { if(tree[rt].num == x) {--tree[rt].cnt, --tree[rt].size;return;} del(tree[rt].ch[tree[rt].num < x], x); push_up(rt); } } int n, opt, a, rec, rt, c; int main() { using namespace SBT; //freopen("test.out", "w", stdout); scanf("%d", &n); SBT::insert(a, opt, inf, 0); while(n--) { scanf("%d%d", &opt, &a); if(opt == 1) { rt = insert(root, rec, a, 0); if(~rt) ReBuild(rt, rec); }else if(opt == 2) del(root, a); else if(opt == 3) printf("%d\n", qrank(a, c) + 1); else if(opt == 4) printf("%d\n", qnum(a)); else if(opt == 5) printf("%d\n", qnum(qrank(a, c))); else printf("%d\n", qnum(rec = qrank(a, c) + c + 1)); } return 0; }
没有帐号? 立即注册