前言
- msjing在sbmqwm的压力下去学了 \(Splay\),现在她学成(?)归来了
介绍
- \(Splay\),又称伸展树。是依据 \(Splay\) 操作保持平衡的平衡树,操作是单 \(\log_n\) 的
但好像常数也是起飞的,不会退化成链,仍然依据 \(BST\) 来维护
实现和维护
维护的变量
fa:父亲
ch[0] & ch[1]:左右儿子
val:权值
cnt:权值出现次数
siz:所在子树大小
\(Splay\)
- \(Splay\) 操作是最关键的部分,由于 \(Splay\) 操作才能保持平衡,在这之前,我们先说旋转操作
旋转
- 旋转操作是很多平衡树维持平衡的方法,非常难理解,msjing完全搞不懂呢,sbmqwm你够了别搞a~
和一位
- 旋转还要依据 \(BST\),保证中序遍历有序,总共有两种操作:左旋和右旋
继续偷图

- 我们来说一下是如何操作的,以右旋为例
- 把 2 提上去,1 放下来,但是我们发现 2 有三个儿子,这不行,我们可以依据性质把 5 给到 1,就符合了,可以手动跑一下中序遍历
- 具体怎么搞,e,msjing并不是挺会,给代码吧
void Rotate(int x)
{
int y=st[x].fa,z=st[y].fa,k=st[y].ch[1] == x;
st[z].ch[st[z].ch[1] == y]=x;
st[x].fa=z;
st[y].ch[k]=st[x].ch[k^1];
st[st[x].ch[k^1]].fa=y;
st[x].ch[k^1]=y;
st[y].fa=x;
Pushup(y),Pushup(x);
}
- 等一下我先把sbmqwm赶出去
- 那个
Pushup,就是非常普通算 \(siz\) 的
\(Splay\)
- 接下来就是最重要的操作了,分 3 步:\(zig\),\(zig\)-\(zig\),\(zig\)-\(zag\)
\(zig\)
- \(zig\) 操作,指操作根节点,直接旋

依旧偷图
\(zig\)-\(zig\)
- \(zig\)-\(zig\) 操作,即不是操作的根节点,且操作点都是左儿子,e,说不清,看图就行,需要两次操作,先把 \(y\) 提上来,再提 \(x\),相当于利用 \(x\) 的父亲提 \(x\)

\(zig\)-\(zag\)
- \(zig\)-\(zag\) 操作,还是操作的节点不是根节点,但是不是上述的都是左儿子,而是有左有右,呈折线状

- 三个操作就讲完了,为什么只讲左儿子捏,因为右边是对称的
- 这个东西为什么是对的,msjing没学过势能分析,搞不了,e,去oiwiki看看吧,msjing真不会证,反正这么做就是对的
void Splay(int x,int k)
{
while (st[x].fa!=k)
{
int y=st[x].fa,z=st[y].fa;
if (z!=k) (st[z].ch[0] == y)^(st[y].ch[0] == x) ? Rotate(x) : Rotate(y);
Rotate(x);
}
if (k == 0) root=x;
}
其他操作
- 好多就和 \(FHQ\) 很像了,可以到这里看看
- e,给代码吧,msjing累了,咕咕咕
void fid(int x)
{
int u=root;
if (!u) return;
while (st[u].ch[x>st[u].val] && x!=st[u].val) u=st[u].ch[x>st[u].val];
Splay(u,0);
}
void Insert(int x)
{
int u=root,fa=0;
while (u && st[u].val!=x) fa=u,u=st[u].ch[x>st[u].val];
if (u) st[u].cnt++;
else
{
u=++tot;
if (fa) st[fa].ch[x>st[fa].val]=u;
st[u].ch[0]=st[u].ch[1]=0;
st[tot].fa=fa;st[tot].val=x;
st[tot].cnt=1;st[tot].siz=1;
}
Splay(u,0);
}
int nxt(int x,int f)
{
fid(x);
int u=root;
if ((st[u].val>x && f) || (st[u].val<x &&!f)) return u;
u=st[u].ch[f];
while (st[u].ch[f^1]) u=st[u].ch[f^1];
return u;
}
void Delete(int x)
{
int lt=nxt(x,0),net=nxt(x,1);
Splay(lt,0),Splay(net,lt);
int del=st[net].ch[0];
if (st[del].cnt>1) st[del].cnt--,Splay(del,0);
else st[net].ch[0]=0;
}
int kth(int x)
{
int u=root;
if (st[u].siz<x) return 0;
while (1)
{
int y=st[u].ch[0];
if (x>st[y].siz+st[u].cnt) x-=st[y].siz+st[u].cnt,u=st[u].ch[1];
else
{
if (st[y].siz>=x) u=y;
else return st[u].val;
}
}
}
int rak(int x)
{
Insert(x);
fid(x);
int k=st[st[root].ch[0]].siz;
Delete(x);
return k;
}
总代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
constexpr int p=100003,maxn=2e5+10;
long long read() {long long x=0,f=1;char ch=getchar();while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}
int n;
namespace splay
{
struct _ {int fa,cnt,ch[2],val,siz;}st[maxn];
int tot,root;
void Pushup(int x) {st[x].siz=st[st[x].ch[0]].siz+st[st[x].ch[1]].siz+st[x].cnt;}
void Rotate(int x)
{
int y=st[x].fa,z=st[y].fa,k=st[y].ch[1] == x;
st[z].ch[st[z].ch[1] == y]=x;
st[x].fa=z;
st[y].ch[k]=st[x].ch[k^1];
st[st[x].ch[k^1]].fa=y;
st[x].ch[k^1]=y;
st[y].fa=x;
Pushup(y),Pushup(x);
}
void Splay(int x,int k)
{
while (st[x].fa!=k)
{
int y=st[x].fa,z=st[y].fa;
if (z!=k) (st[z].ch[0] == y)^(st[y].ch[0] == x) ? Rotate(x) : Rotate(y);
Rotate(x);
}
if (k == 0) root=x;
}
void fid(int x)
{
int u=root;
if (!u) return;
while (st[u].ch[x>st[u].val] && x!=st[u].val) u=st[u].ch[x>st[u].val];
Splay(u,0);
}
void Insert(int x)
{
int u=root,fa=0;
while (u && st[u].val!=x) fa=u,u=st[u].ch[x>st[u].val];
if (u) st[u].cnt++;
else
{
u=++tot;
if (fa) st[fa].ch[x>st[fa].val]=u;
st[u].ch[0]=st[u].ch[1]=0;
st[tot].fa=fa;st[tot].val=x;
st[tot].cnt=1;st[tot].siz=1;
}
Splay(u,0);
}
int nxt(int x,int f)
{
fid(x);
int u=root;
if ((st[u].val>x && f) || (st[u].val<x &&!f)) return u;
u=st[u].ch[f];
while (st[u].ch[f^1]) u=st[u].ch[f^1];
return u;
}
void Delete(int x)
{
int lt=nxt(x,0),net=nxt(x,1);
Splay(lt,0),Splay(net,lt);
int del=st[net].ch[0];
if (st[del].cnt>1) st[del].cnt--,Splay(del,0);
else st[net].ch[0]=0;
}
int kth(int x)
{
int u=root;
if (st[u].siz<x) return 0;
while (1)
{
int y=st[u].ch[0];
if (x>st[y].siz+st[u].cnt) x-=st[y].siz+st[u].cnt,u=st[u].ch[1];
else
{
if (st[y].siz>=x) u=y;
else return st[u].val;
}
}
}
int rak(int x)
{
Insert(x);
fid(x);
int k=st[st[root].ch[0]].siz;
Delete(x);
return k;
}
}
int main()
{
n=read();
splay::Insert(1e9),splay::Insert(-1e9);
while (n--)
{
int op=read(),x=read();
if (op == 1) splay::Insert(x);
if (op == 2) splay::Delete(x);
if (op == 3) printf("%d\n",splay::rak(x));
if (op == 4) printf("%d\n",splay::kth(x+1));
if (op == 5) printf("%d\n",splay::st[splay::nxt(x,0)].val);
if (op == 6) printf("%d\n",splay::st[splay::nxt(x,1)].val);
}
return 0;
}
后话
- \(Splay\) 这个msjing学的不是很清,主要会用 \(FHQ\) 就行,学 \(Splay\) 其实是为了动态树,以后有时间再补吧,咕咕咕
- sbmqwm:宝你是鸽子吗