普通平衡树——Scapegoat _ Tree(替罪羊树)/splay(伸展树)

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

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

题目:

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

  1. 插入 x 数;

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

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

  4. 查询排名为 x 的数;

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

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

解题报告:

代码已注释。

1.替罪羊树

  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

2.splay

  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 using namespace std;
 14 template <typename T>
 15 void read(T &res) {
 16     bool flag=false;char ch;
 17     while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true);
 18     for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm);
 19     flag&&(res=-res);
 20 }
 21 template <typename T>
 22 void write(T x) {
 23     if(x<0) putchar('-'),x=-x;
 24     if(x>9) write(x/10);
 25     putchar(x%10+'0');
 26 }
 27 const int maxn=1e6+10;
 28 #define il inline
 29 int cnt[maxn],sze[maxn],ch[maxn][2],key[maxn],f[maxn],sz,root;
 30 ///key[x]:结点x原本的大小
 31 ///cnt[x]:当前结点存的权值(key[x]的个数)
 32 ///sze[x]:当前结点x的子树(包含x)大小
 33 ///ch[x][0]:左儿子、ch[x][1]:右儿子
 34 ///f[x]:x的父亲
 35 ///sz:整棵树的大小
 36 il void init(int n) {///n为操作总数
 37     sz=root=0;
 38     for(int i=0;i<=n;i++)
 39         ch[i][0]=ch[i][1]=f[i]=sze[i]=key[i]=cnt[i]=0;
 40 }
 41 il void clearly(int x) {///用于删除之后
 42     cnt[x]=sze[x]=ch[x][0]=ch[x][1]=f[x]=key[x]=0;
 43 }
 44 il void update(int x) {///更新当前结点的子树大小
 45     if(x) {
 46         sze[x]=cnt[x];
 47         sze[x]+=ch[x][0]?sze[ch[x][0]]:0;
 48         sze[x]+=ch[x][1]?sze[ch[x][1]]:0;
 49     }
 50 }
 51 il int getf(int x) {///判断结点x是他父亲的左儿子还是右儿子
 52     return ch[f[x]][1]==x;
 53 }
 54 il void rotated(int x) {///将x旋转到其父节点
 55     int old=f[x],oldf=f[old],which=getf(x);
 56     ch[old][which]=ch[x][which^1];f[ch[old][which]]=old;
 57     f[old]=x;ch[x][which^1]=old;
 58     f[x]=oldf;
 59     if(oldf)   ///如果爷爷结点存在的话,要连接x
 60         ch[oldf][ch[oldf][1]==old]=x;
 61     update(old);update(x);///先update下面的结点:old,然后是x
 62 }
 63 il void splay(int x) {///将x一直rotate到根
 64     for(int fa;fa=f[x];rotated(x))
 65         if(f[fa])///如果三点一线要先rotatex的父亲
 66             rotated((getf(fa)==getf(x))?fa:x);
 67     root=x;///attention!!!
 68 }
 69 il void inserted(int x) {
 70     if(!root) {
 71         sz++;
 72         f[sz]=ch[sz][0]=ch[sz][1]=0;
 73         cnt[sz]=sze[sz]=1;
 74         key[sz]=x;
 75         root=sz;    ///attention!!!
 76         return ;
 77     }
 78     int now=root,fa=0;
 79     while(1) {
 80         if(key[now]==x) {
 81             cnt[now]++;
 82             update(now);
 83             splay(now);
 84             break;
 85         }
 86         fa=now;
 87         now=ch[now][key[now]<x];
 88         if(!now) {
 89             sz++;
 90             ch[sz][0]=ch[sz][1]=0;
 91             cnt[sz]=sze[sz]=1;
 92             key[sz]=x;
 93             f[sz]=fa;
 94             ch[fa][key[fa]<x]=sz;
 95             update(fa);
 96             splay(sz);
 97             break;
 98         }
 99     }
100 }
101 il int x_find_pos(int x) {
102     int ans=0,now=root;
103     while(true) {
104         if(x<key[now])
105             now=ch[now][0];
106         else {
107             ans+=(ch[now][0]?sze[ch[now][0]]:0);
108             if(x==key[now]) {
109                 splay(now);
110                 return ans+1;
111             }
112             ans+=cnt[now];
113             now=ch[now][1];
114         }
115     }
116 }
117 il int pos_find_x(int pos) {
118     int now=root;
119     while(true) {
120         if(ch[now][0]&&pos<=sze[ch[now][0]])
121             now=ch[now][0];
122         else {
123             int temp=(ch[now][0]?sze[ch[now][0]]:0)+cnt[now];
124             if(pos<=temp)
125                 return key[now];
126             pos-=temp;
127             now=ch[now][1];
128         }
129     }
130 }
131 il int pre() {
132     int now=ch[root][0];
133     while(ch[now][1]) now=ch[now][1];
134     return now;
135 }
136 il int net() {
137     int now=ch[root][1];
138     while(ch[now][0]) now=ch[now][0];
139     return now;
140 }
141 il void del(int x) {///删除一个权值为x的数
142     x_find_pos(x);///目的是把x提到树根
143     if(cnt[root]>1) {
144         cnt[root]--;
145         update(root);
146         return ;
147     }
148     if(!ch[root][0]&&!ch[root][1]) {
149         clearly(root);
150         root=0;///attention!!!
151         return;///只有一个点的情况
152     }
153     if(!ch[root][0]) {
154         int oldroot=root;
155         root=ch[root][1];
156         f[root]=0;
157         clearly(oldroot);
158         return ;
159     }
160     if(!ch[root][1]) {
161         int oldroot=root;
162         root=ch[root][0];
163         f[root]=0;
164         clearly(oldroot);
165         return ;
166     }
167     int leftbig=pre();///找到前驱所在位置
168     int oldroot=root;
169     splay(leftbig);///把前驱提到树根,目的:使根结点成为他的左儿子
170     f[ch[oldroot][1]]=root;
171     ch[root][1]=ch[oldroot][1];
172     clearly(oldroot);
173     update(root);///attention!!!
174     return ;
175 }
176 int main()
177 {
178     int n;
179     while(scanf("%d",&n)!=EOF) {
180         int op,x;
181         init(n);
182         for(int i=1;i<=n;i++) {
183             read(op),read(x);
184             if(op==1) inserted(x);
185             else if(op==2) del(x);
186             else if(op==3) write(x_find_pos(x)),pn;
187             else if(op==4) write(pos_find_x(x)),pn;
188             else if(op==5) inserted(x),write(key[pre()]),pn,del(x);
189             else if(op==6) inserted(x),write(key[net()]),pn,del(x);
190         }
191         pn;
192     }
193     return 0;
194 }
View Code

 

 

 

 

posted @ 2019-10-31 00:28  wuliking  阅读(276)  评论(0编辑  收藏  举报