感谢yyb大佬的博客!
splay,初看像是个十分羞耻高逼格的名字,splay也确实是一个神级的数据结构,虽然同机房大佬rayment不停推荐treap,但我还是决定先写一波splay
splay不得不介绍的,便是它的旋转(rotate)操作了,双旋splay可以毫无压力的将二叉查找树保持平衡,从而轻易使查找等操作保持在O(logn)的复杂度。
stl大法好!有像set、rope这样的东西,我要它有何用?但手打毕竟还是更加好的,很多操作都可以轻松实现。
such as 求出区间和/滑稽(这特么不是线段树该干的事吗?)
下面我们简单展示一下rotate操作,像splay操作、delete操作的话有些小复杂,蒟蒻不好描述,泥萌可以找到一些好博客看一看QWQ(留坑)
这就是旋转过程啦!虽然并没有使树的深度变短,但却skr很好的例子。
图中,你们可以发现两张图的先序遍历是没有区别de。此图是将a左转了一下。y就需要跑到a的右子书去,但喜闻乐见的是,a的右子树还挺抢手。那么,我们就可以将d丢到y的左子树上,再把y-b扯出来,放到a的右子树
似不似很简单!
下面是模板题普通平衡树的代码
#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int ch[maxn][2],f[maxn],key[maxn],cnt[maxn],size[maxn];//我们选择直接把splay放外面,ps.cnt表示该值出现了几次,而size表示以i为根的子树大小
int tot=0,root;//tot表示树的大小,root是树的根
inline int get(int x){
if (ch[f[x]][0]==x)return 0;
else return 1;//这里是看x是左子树还是右子树
}
inline void pushup(int u){
size[u]=size[ch[u][0]]+size[ch[u][1]]+cnt[u];
}//更新u的size与cnt
inline void rotate(int x){
int fa=f[x],old=f[fa];
int w=get(x);//它是左子树,还是右子树?
ch[old][ch[old][1]==fa]=x;//爷爷的儿子变孙子
f[x]=old;//爸爸变爷爷
ch[fa][w]=ch[x][w^1];//儿子的儿子变爸爸的儿子
f[ch[x][w^1]]=fa;//儿子的儿子的爸爸变爸爸的爸爸
ch[x][w^1]=fa;//儿子的爸爸变儿子
f[fa]=x;//爸爸的儿子变爸爸
pushup(fa);pushup(x);、、更新
}
inline void splay(int x,int goal){
for (;f[x]!=goal;rotate(x)){
int fa=f[x];
if (f[fa]!=goal)
rotate((get(x)==get(fa))?fa:x);
}
if (goal==0) root=x;//双旋splay
}
inline void insert(int v){
int x=root,fa=0;
while (x&&key[x]!=v){
fa=x;
x=ch[x][v>key[x]];
}
if (x) cnt[x]++;//如果x已经存在,那么cnt[x]++;
else {
tot++;
x=tot;
if (fa)
ch[fa][v>key[fa]]=x;
ch[x][0]=ch[x][1]=0;
f[tot]=fa;
key[tot]=v;
cnt[tot]=size[tot]=1;//记得要将cnt和size更新!
}
splay(x,0);
}//二叉查找树的标准插♂入,只是记得要将插入点splay到根来保持平衡
inline void find(int x){
int r=root;
if (r==0)return ;
while(ch[r][x>key[r]]&&x!=key[r])
r=ch[r][x>key[r]];
splay(x,0);
}
inline int next(int x){
find(x);
int r=root;
if(key[r]>x)return r;
r=ch[r][1];
while(ch[r][1])r=ch[r][1];
return r;
}
inline int pre(int x){
find(x);
int r=root;
if(key[r]<x)return r;
r=ch[r][0];
while(ch[r][0])r=ch[r][0];
return r;
}
inline void del(int x){
int up=next(x);
int down=pre(x);
splay(up,0);splay(down,up);
int u=ch[down][0];
if (cnt[u]>1){
cnt[u]--;
splay(u,0);
}
else ch[down][0]=0;
}
inline int kth(int x)
{
int u=root;
if(size[u]<x)
return 0;
while(1)
{
int y=ch[u][0];
if(x>size[y]+cnt[u])
{
x-=size[y]+cnt[u];
u=ch[u][1];
}
else
if(size[y]>=x)
u=y;
else
return key[u];
}
} //yyb大佬的kth与del,我还太弱只能照着写
int main(){
int i,j,n;
cin>>n;
insert(2147483647);
insert(-2147483647);
for (i=1;i<=n;i++){
int x,y;
cin>>x>>y;
if (x==1)
insert(y);
if (x==2)
del(y);
if (x==3)
printf("%d\n",size[ch[root][0]]);
if (x==4)
cout<<kth(x+1)<<endl;
if (x==5)
cout<<key[pre(y)]<<endl;
if (x==6)
cout<<key[next(y)]<<endl;
}
return 0;
}
此代码还未调试结束qwq,所以是错的。。