【洛谷P3369】【模板】普通平衡树题解

题目链接

题意:

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

输入格式:

第一行为n,表示操作的个数。下面n行每行有两个整数opt和x,opt表示操作的序号

输出格式:

对于操作3,4,5,6每行输出一个数,表示对应答案

样例输入:

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

样例输出:

106465
84185
492737

时空限制:

每个测试点1s,128MB

数据范围:

n≤105

|x|≤107

1≤opt≤6

题解:

首先来讲讲什么是Treap……

Treap的中文名叫树堆(Tree+Heap),同时拥有权值和优先级;对于权值来说,它是一棵二叉搜索树(BST);对于优先级来说,它是一个堆。它可以被当作平衡树来用。

我们希望平衡树的高度尽量小,因为只有这样才能使每次操作的均摊时间复杂度趋于O(logn)。为了避免极端数据使平衡树退化为链,导致时间复杂度退化为O(n),我们可以想到用优先级来维护Treap的形态使它的高度尽量趋于平衡,因为当每个节点的权值和优先级确定时,Treap的形态是唯一确定的。我们把每个节点的优先级用随机数rand()来赋值,这样Treap就趋于平衡了。

详细代码如下(指针实现):

  1 #include<cstdio>
  2 #include<ctime>
  3 #include<cstdlib>
  4 struct node{
  5     node*ch[2];//指向左(ch[0])右(ch[1])子节点的指针
  6     int v;//权值
  7     int r;//优先级
  8     int cnt;//该节点数的个数(数可以重复)
  9     int size;//以该节点为根的子树中数的个数
 10     node(int vv){//新建节点的构造函数
 11         ch[0]=ch[1]=NULL;//两指针初始化为空指针
 12         v=vv;//权值初始化
 13         r=rand();//优先级用随机数初始化
 14         cnt=size=1;//只含一个数
 15     }
 16 };
 17 node*super;//指向整棵Treap的大根的指针
 18 int n,ans;
 19 inline void maintain(node*p){//维护该节点的附加信息
 20     p->size=p->cnt;
 21     if(p->ch[0]!=NULL)p->size+=p->ch[0]->size;
 22     if(p->ch[1]!=NULL)p->size+=p->ch[1]->size;
 23 }
 24 inline void rot(node*&p,int lr){//旋转操作(lr==0:左旋,lr==1:右旋)
 25     node*k=p->ch[lr^1];
 26     p->ch[lr^1]=k->ch[lr];
 27     k->ch[lr]=p;
 28     p=k;
 29 }
 30 void ins(node*&p,int val){//插入操作:在以p为根的子树中插入一个val数
 31     if(p==NULL){//空节点直接建新点
 32         p=new node(val);
 33     }else if(val==p->v){//找到了已经存在的节点
 34         p->cnt++;
 35         p->size++;
 36     }else if(val<p->v){
 37         ins(p->ch[0],val);//在左子节点中插入
 38         if(p->ch[0]->r<p->r){//维护优先级(小根堆)
 39             rot(p,1);
 40             maintain(p->ch[1]);
 41         }
 42         maintain(p);
 43     }else{
 44         ins(p->ch[1],val);//在右子节点中插入
 45         if(p->ch[1]->r<p->r){
 46             rot(p,0);
 47             maintain(p->ch[0]);
 48         }
 49         maintain(p);
 50     }
 51 }
 52 void del(node*&p,int val){//删除操作:在以p为根的子树中删除一个val数
 53     if(val<p->v){
 54         del(p->ch[0],val);
 55         maintain(p);
 56     }else if(val>p->v){
 57         del(p->ch[1],val);
 58         maintain(p);
 59     }else{
 60         if(p->cnt>1){//该节点包含多个数,直接减即可
 61             p->cnt--;
 62             p->size--;
 63         }else{//删除该节点
 64             if(p->ch[0]!=NULL){
 65                 if(p->ch[1]!=NULL){//该节点有左右子节点
 66                     int lr=p->ch[0]->r<p->ch[1]->r?0:1;//选优先级小的子节点为根(小根堆)
 67                     rot(p,lr^1);//旋转
 68                     del(p->ch[lr^1],val);//递归向下删除
 69                     maintain(p);//递归返回向上维护
 70                 }else{//无右子节点
 71                     node*pp=p;
 72                     p=p->ch[0];
 73                     delete pp;
 74                 }
 75             }else{//无左子节点
 76                 if(p->ch[1]!=NULL){
 77                     node*pp=p;
 78                     p=p->ch[1];
 79                     delete pp;
 80                 }else{//都无
 81                     delete p;
 82                     p=NULL;
 83                 }
 84             }
 85         }
 86     }
 87 }
 88 int qrank(node*p,int val){//当前以p为根的子树中小于val的数的个数+1
 89     int ssl=p->ch[0]==NULL?0:p->ch[0]->size;//该子树中小于val的数的个数
 90     if(val==p->v)return ssl+1;
 91     else if(val<p->v){
 92         if(p->ch[0]!=NULL)return qrank(p->ch[0],val);
 93         else return 1;
 94     }else{
 95         if(p->ch[1]!=NULL)return ssl+p->cnt+qrank(p->ch[1],val);
 96         else return p->size+1;
 97     }
 98 }
 99 int qval(node*p,int rank){//在当前子树中查找排名为rank的数
100     int ssl=p->ch[0]==NULL?0:p->ch[0]->size;
101     if(rank<=ssl)return qval(p->ch[0],rank);
102     else if(rank<=ssl+p->cnt)return p->v;
103     else return qval(p->ch[1],rank-ssl-p->cnt);
104 }
105 void pred(node*p,int val){//在当前子树中查找val的前驱
106     if(val<=p->v){
107         if(p->ch[0]!=NULL)pred(p->ch[0],val);
108     }else{
109         ans=p->v;//ans为暂定的答案,当ans无法再修改时,ans为最终答案
110         if(p->ch[1]!=NULL)pred(p->ch[1],val);
111     }
112 }
113 void succ(node*p,int val){
114     if(val>=p->v){
115         if(p->ch[1]!=NULL)succ(p->ch[1],val);
116     }else{
117         ans=p->v;
118         if(p->ch[0]!=NULL)succ(p->ch[0],val);
119     }
120 }
121 int main(){
122     srand(time(0));//初始化随机数种子
123     scanf("%d",&n);
124     while(n--){
125         int opt,x;
126         scanf("%d%d",&opt,&x);
127         switch(opt){
128             case 1:{
129                 ins(super,x);
130                 break;
131             }
132             case 2:{
133                 del(super,x);
134                 break;
135             }
136             case 3:{
137                 printf("%d\n",qrank(super,x));
138                 break;
139             }
140             case 4:{
141                 printf("%d\n",qval(super,x));
142                 break;
143             }
144             case 5:{
145                 pred(super,x);
146                 printf("%d\n",ans);
147                 break;
148             }
149             case 6:{
150                 succ(super,x);
151                 printf("%d\n",ans);
152                 break;
153             }
154         }
155     }
156     return 0;
157 }
posted on 2018-09-06 20:50  AndyGamma  阅读(438)  评论(0编辑  收藏  举报