学习链接:Splay - 小蒟蒻laf - 博客园
模板题:【模板】普通平衡树 - 洛谷
暂时只抄了个板子,基本理解了代码,内容还有待补充。
#include<bits/stdc++.h>
using namespace std;
const int N=100005,INF=2147483647;
int cnt[N],val[N],sz[N],fa[N],ch[N][2],root,tot;
// ! ch[x][0]:left
// ! ch[x][1]:right
inline int Get(int x) { return ch[fa[x]][1]==x; } //该点是否为父亲的右孩子
inline void Up(int x) { sz[x]=cnt[x]+sz[ch[x][0]]+sz[ch[x][1]]; } //维护sz(sz[u]表示子树大小)
inline void Rot(int x) { //对x结点进行一次单旋转
register int y=fa[x],z=fa[y],k=Get(x);
ch[z][Get(y)]=x,fa[x]=z;
ch[y][k]=ch[x][k^1],fa[ch[x][k^1]]=y;
ch[x][k^1]=y,fa[y]=x;
Up(y),Up(x);
}
inline void Splay(int x,int goal=0) { //把x结点旋转到goal的下面(默认情况下goal是0,也就是让x为根)
for(int y;(y=fa[x])^goal;Rot(x)) //不断rot,直到x在goal的下面
if(fa[y]^goal)Rot(Get(x)^Get(y)?x:y); //但这里要注意,如果链比较长,那就先旋转父节点,再旋转当前结点
if(!goal)root=x; //goal=0的情况下,x作为根
}
inline void Find(int x) { //寻找值最接近x的结点,并且让这个点变成根
if(!root)return;
register int u=root; //要求寻找root的子树内的结果,所以从u=root开始
while(ch[u][x>val[u]] && x^val[u]) //还没找到x,往下走
u=ch[u][x>val[u]];
Splay(u);
}
inline void Ins(int x) { //插入x,并且让x所在点变成根
register int u=root,fu=0; //当前结点,父节点
while(u && val[u]^x)fu=u,u=ch[u][x>val[u]]; //往下走
if(u)cnt[u]++; //出现过,那就只需要cnt++
else { //没出现过,新建结点
u=++tot;
if(fu)ch[fu][x>val[fu]]=u; //父亲进行child连接
ch[u][0]=ch[u][1]=0; //新结点没有儿子
fa[u]=fu,val[u]=x; //更新fa和val
cnt[u]=sz[u]=1; //出现次数和子树大小,初始是1
}
Splay(u);
}
inline int Nxt(int x,int f) {
Find(x); //寻找x所在结点编号,记录在root处
register int u=root; //让u等于root,即x所在结点编号
if(val[u]>x && f)return u; //最接近x的数比x大,我们恰好要找最小的x大的数,直接返回这个结点编号
if(val[u]<x &&!f)return u; //同上
u=ch[u][f]; //不满足,下面两行就是继续寻找另一个方向最接近的(自行理解一下)
while(ch[u][f^1])u=ch[u][f^1];
return u;
}
inline void Del(int x) { //删除一个x
register int lst=Nxt(x,0),nxt=Nxt(x,1); //前驱和后继
Splay(lst),Splay(nxt,lst); //让lst做根,nxt做lst的右儿子,这样就一定能使得nxt的左儿子是x!
register int del=ch[nxt][0]; //del一定就是x所在结点
if(cnt[del]>1)--cnt[del],Splay(del); //如果删完还有,要继续Splay一下
else ch[nxt][0]=0; //如果删完没有了,那就置空nxt的左指针
}
inline int Kth(int k) { //输出第k大的数
register int u=root,sn=0; //当前结点,左儿子结点
for(;;) {
sn=ch[u][0];
if(k>sz[sn]+cnt[u])
k-=sz[sn]+cnt[u],u=ch[u][1];
else if(sz[sn]>=k)u=sn;
else return val[u];
}
}
int T;
int main() {
Ins(-INF); //加入两个哨兵结点,防止越界的情况出现
Ins(+INF);
scanf("%d",&T);
for(int opt,x;T--;) {
scanf("%d%d",&opt,&x);
if(opt==1)Ins(x); //插入x
else if(opt==2)Del(x); //删除一个x
else if(opt==3)Find(x),printf("%d\n",sz[ch[root][0]]); //让x所在结点作为根,那么根的左子树大小+1就是排名,但是由于有-INF,又要-1,综合起来就是不用加也不用减
else if(opt==4)printf("%d\n",Kth(x+1)); //输出排名第k+1的数(因为有个-INF的影响,所以要+1)
else if(opt==5)printf("%d\n",val[Nxt(x,0)]); //x的前驱
else if(opt==6)printf("%d\n",val[Nxt(x,1)]); //x的后继
}
}
to be continued...