学习自:https://www.luogu.org/blog/user28084/solution-p3369
简述:看了一个下午+晚上搞出来的东西。
题目:
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
-
插入 x 数;
-
删除 x 数(若有多个相同的数,因只删除一个);
-
查询 x 数的排名(若有多个相同的数,因输出最小的排名);
-
查询排名为 x 的数;
-
求 x 的前趋(前趋定义为小于 x ,且最大的数);
-
求 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('
')
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 }
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('
')
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 }