普通平衡树——Scapegoat _ Tree(替罪羊树)

学习自:https://www.luogu.org/blog/user28084/solution-p3369

简述:看了一个下午+晚上搞出来的东西。

题目:

  您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入 x 数;

  2. 删除 x 数(若有多个相同的数,因只删除一个);

  3. 查询 x 数的排名(若有多个相同的数,因输出最小的排名);

  4. 查询排名为 x 的数;

  5. 求 x 的前趋(前趋定义为小于 x ,且最大的数);

  6. 求 x 的后继(后继定义为大于 x ,且最小的数)。

解题报告:代码即解释。

  1 /*替罪羊树
  2 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
  3 1.插入 x 数;
  4 2.删除 x 数(若有多个相同的数,因只删除一个);
  5 3.查询 x 数的排名(若有多个相同的数,因输出最小的排名);
  6 4.查询排名为 x 的数;
  7 5.求 x 的前趋(前趋定义为小于 x ,且最大的数);
  8 6.求 x 的后继(后继定义为大于 x ,且最小的数)。*/
  9 #include<bits/stdc++.h>
 10 #define numm ch-48
 11 #define pd putchar(' ')
 12 #define pn putchar('\n')
 13 #define pb push_back
 14 #define debug(args...) cout<<#args<<"->"<<args<<endl
 15 #define bug cout<<"************"
 16 using namespace std;
 17 template <typename T>
 18 void read(T &res) {
 19     bool flag=false;char ch;
 20     while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true);
 21     for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm);
 22     flag&&(res=-res);
 23 }
 24 template <typename T>
 25 void write(T x) {
 26     if(x<0) putchar('-'),x=-x;
 27     if(x>9) write(x/10);
 28     putchar(x%10+'0');
 29 }
 30 typedef long long ll;
 31 typedef long double ld;
 32 const int maxn=2e5+10;
 33 const ll mod=1e9+7;
 34 const int inf=0x3f3f3f3f;
 35 const double alpha=0.7;
 36 #define il inline
 37 struct node {
 38     int exist,son[2],sze,valid,val;
 39     ///valid:当前子树的真实大小
 40     ///sze:当前子树的虚假大小
 41     ///exist:当前节点是否存在
 42     ///val:当前节点的权值
 43     ///son[0]:左儿子,son[1]:右儿子
 44 }e[maxn];
 45 int memory[maxn],cur[maxn],mpos,cpos,to_rebuild,root;
 46 ///memory[]:内存池(下标mpos);cur[]:存放要重建的树的中序遍历(下标cpos)
 47 il bool isbad(int now) {   ///以当前节点为根的子树是否需要重建
 48     if((double)e[now].valid*alpha<=(double)max(e[e[now].son[0]].valid,e[e[now].son[1]].valid))
 49         return true;    ///需要重建
 50     else return false;
 51 }
 52 il void build(int l,int r,int &now) {///对有序序列进行重构二叉树
 53     int mid=l+r>>1;
 54     now=cur[mid];   ///每次都相当于把中间这个数提出来
 55     if(l==r) {
 56         e[now].son[0]=e[now].son[1]=0;
 57         e[now].valid=e[now].sze=1;
 58         return ;
 59     }
 60     if(l<mid) build(l,mid-1,e[now].son[0]); ///mid这个结点已经建完了
 61     else e[now].son[0]=0;
 62     build(mid+1,r,e[now].son[1]);///这里为什么不用判呢?想一下l=3,r=4的情况
 63     e[now].sze=e[e[now].son[0]].sze+e[e[now].son[1]].sze+1; ///+1要划重点!!!
 64     e[now].valid=e[e[now].son[0]].valid+e[e[now].son[1]].valid+1;///+1就是加本身
 65     return ;
 66 }
 67 il void dfs(int now) { ///求中序遍历
 68     if(!now) return ;
 69     dfs(e[now].son[0]); ///中序遍历先遍历左子树
 70     if(e[now].exist) cur[++cpos]=now;///存在的结点压入cur
 71     else memory[++mpos]=now;///不存在的则回收
 72     dfs(e[now].son[1]);
 73     return ;
 74 }
 75 il void rebuild(int &now) {///重建二叉树
 76     cpos=0;     ///attention!!!
 77     dfs(now);
 78     if(cpos) build(1,cpos,now);
 79     else now=0; ///没有要重建的,now=0
 80 }
 81 il void ins(int &now,int val) {    ///插入一个数
 82     if(!now) {
 83         now=memory[mpos--]; ///分配一个空间给当前结点,作为它的下标
 84         e[now].val=val;
 85         e[now].exist=e[now].valid=e[now].sze=1;
 86         e[now].son[0]=e[now].son[1]=0;
 87         return ;
 88     }
 89     e[now].sze++,e[now].valid++;
 90     if(val<=e[now].val) ins(e[now].son[0],val);///比当前的值大往右,否则往左
 91     else ins(e[now].son[1],val);
 92     if(!isbad(now)) {   ///如果回溯到当前节点不用重建,就判断在它之前回溯的是否有需要重建的
 93         if(to_rebuild) {
 94             if(to_rebuild==e[now].son[0]) rebuild(e[now].son[0]);///判断左右子树哪个需要重建
 95             else rebuild(e[now].son[1]);
 96             to_rebuild=0;
 97         }
 98     }
 99     else to_rebuild=now;///当前节点需要重建,先不重建,继续回溯
100 }
101 il int find_rnk(int val) {///查找权值为val对应的排名
102     int ans=1,now=root;
103     while(now) {
104         if(e[now].val>=val) now=e[now].son[0];
105         else {  ///下面两句话不要写颠倒!!!
106             ans+=e[e[now].son[0]].valid+e[now].exist;
107             now=e[now].son[1];
108         }
109     }
110     return ans;
111 }
112 il int find_val(int k) {   ///查找排名为k对应的权值
113     int now=root;
114     while(now) {
115         if(e[now].exist&&e[e[now].son[0]].valid+1==k) return e[now].val;
116         if(e[e[now].son[0]].valid>=k) now=e[now].son[0];
117         else {  ///下面两句话不要写颠倒!!!
118             k=k-e[e[now].son[0]].valid-e[now].exist;
119             now=e[now].son[1];
120         }
121     }
122 }
123 il void del_rnk(int &now,int k) {  ///删除排名为k的结点
124     if(e[now].exist&&e[e[now].son[0]].valid+1==k) {///当前的now为要找的结点
125         e[now].exist=0;
126         e[now].valid--;
127         return ;
128     }
129     e[now].valid--;
130     if(e[e[now].son[0]].valid+e[now].exist>=k) del_rnk(e[now].son[0],k);
131     else del_rnk(e[now].son[1],k-e[e[now].son[0]].valid-e[now].exist);
132     return ;
133 }
134 il void del_val(int val) { ///删除权值为val的数(删除靠左的第一个数)
135     del_rnk(root,find_rnk(val));
136     if((double)e[root].sze*alpha>(double)e[root].valid) rebuild(root);  ///如果删除的结点太多,则也需要重建
137 }
138 
139 int main()
140 {
141     int n,op,val;
142     for(int i=1;i<=maxn-1;i++)
143         memory[i]=i;
144     while(scanf("%d",&n)!=EOF) {
145         root=to_rebuild=0;
146         e[0].valid=0;
147         mpos=(int)2e5-1;   ///内存池下标初始化
148         for(int i=1;i<=n;i++) {
149             read(op);read(val);
150             if(op==1)
151                 ins(root,val);
152             else if(op==2)
153                 del_val(val);
154             else if(op==3)
155                 write(find_rnk(val)),pn;
156             else if(op==4)
157                 write(find_val(val)),pn;
158             else if(op==5)
159                 write(find_val(find_rnk(val)-1)),pn;
160                 ///find_rnk总会找到val最小的rnk,-1就是最后一个比它小的数的rnk
161             else if(op==6)
162                 write(find_val(find_rnk(val+1))),pn;
163                 ///找到比val小(包括val)的数的数量,直接查找该rnk对应的val
164         }
165         pn;
166     }
167     return 0;
168 }
View Code

猜你喜欢

转载自www.cnblogs.com/wuliking/p/11768962.html