原址:https://www.luogu.org/problem/show?pid=3369
P3369 【模板】普通平衡树(Treap/SBT)
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
-
插入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考虑到这里可能会没有前驱或后继节点就特判了)
查询排名:用一个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=5e5+100;
namespace Splay{
struct node{
int cnt,size,num,fa,ch[2];
node(){cnt=size=num=fa=ch[0]=ch[1]=0;}
void init(){cnt=size=num=fa=ch[0]=ch[1]=0;}
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 now){
if(now==0) {return;}
int l=tree[now].ch[0];
int r=tree[now].ch[1];
tree[now].size=tree[l].size+tree[r].size+tree[now].cnt;
}
void rotate(int now){
int p=tree[now].fa;
int a=tree[p].fa;
int l=tree[p].ch[1]==now;//?1:0
int r=l^1;
if(a)
if(tree[a].ch[0]==p) tree[a].ch[0]=now;
else tree[a].ch[1]=now;
tree[p].fa=now,tree[now].fa=a,tree[tree[now].ch[r]].fa=tree[now].ch[r]?p:0;
tree[p].ch[l]=tree[now].ch[r],tree[now].ch[r]=p;
push_up(p);
push_up(now);
}
void splay(int now,int y){
int des=tree[y].fa;
while(tree[now].fa!=des){
int p=tree[now].fa;
int a=tree[p].fa;
if(p!=y){
if((tree[p].ch[0]==now)^(tree[a].ch[0]==p)) rotate(now);
else rotate(p);
}
rotate(now);
}
if(y==root) root=now;
}
void insert(int now,int x,int last){
if(now==0){now=++cnt;tree[now].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 qnode(int now,int type){ // type 0:前缀 1:后缀
splay(now,root);
int p=tree[now].ch[type];
if(p==0) return -1;
while(tree[p].ch[type^1])
p=tree[p].ch[type^1];
return p;
}
void del(int now){
tree[now].cnt--,tree[now].size--;
int p;
if(!tree[now].cnt){
int pr=qnode(now,0);
int su=qnode(now,1);
if(pr==-1 && su==-1){ tree[now].init();root=0;tree[root].init(); return;}
if(pr==-1) splay(su,root),tree[su].ch[0]=0;
else if(su==-1) splay(pr,root),tree[pr].ch[1]=0;
else {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 find(int num){
int now=root;
while(num!=tree[now].num && now)
now=tree[now].ch[tree[now].num<num];
if(!now) return -1;
return now;
}
int qrank(int num){
int now=find(num);
splay(now,root);
return tree[tree[now].ch[0]].size+1;
}
int qnum(int rank){
int now=root;
while(rank<tree[tree[now].ch[0]].size+1 || rank>tree[tree[now].ch[0]].size+tree[now].cnt){
int l=tree[now].ch[0];
int r=tree[now].ch[1];
if(rank>tree[l].size && r) rank-=(tree[l].size+tree[now].cnt),now=r;
else now=l;
}
return tree[now].num;
}
int qrn(int num,int type){
insert(root,num,0);
int ans=tree[qnode(root,type)].num;
del(root);
return ans;
}
}
int main(){
using namespace Splay;
int t;
scanf("%d",&t);
while(t--){
int opr,x;
scanf("%d%d",&opr,&x);
if(opr==1) insert(root,x,0);
if(opr==2) del(find(x));
if(opr==3) printf("%d\n",qrank(x));
if(opr==4) printf("%d\n",qnum(x));
if(opr==5) printf("%d\n",qrn(x,0));
if(opr==6) printf("%d\n",qrn(x,1));
//tree[0].init();
}
return 0;
}