Splay 板子记
前言(本质是废话)
由于今天出现了一下重大事故:
先生问曰:“写过平衡树板子吗?”众学生答曰:“写过。”
而吾乃蒟蒻也,未写之,不知其之妙。
欲答未学,先生竟言:“啊没写过的自己去学吧。”
遂闭嘴,径自点击 OI-Wiki 习之。
看完之后发现:平衡树定义速通了,左右旋速通了,\(Treap\) 又臭又长失败了,\(Splay\) 挺简单原理会了。
但问题就在于一开始打板子就不会了,所以记录一下这篇神神秘秘横空出世的平衡树板子(后来有人问我为什么不用 \(C++\) 自带的平衡树)。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int id,root; // 节点数量,根节点
int fa[2000005],val[2000005],cnt[2000005],siz[2000005],child[2000005][2];
// 父节点,值,个数,子树大小,左右儿子
bool dir(int x){ // 判断 x 是其父亲的哪个儿子
return x==child[fa[x]][1];
}
void pushup(int x){ // 合并信息
siz[x]=cnt[x]+siz[child[x][0]]+siz[child[x][1]];
return;
}
void rotate(int x){ // 旋转
int y=fa[x],z=fa[y];
bool r=dir(x);
child[y][r]=child[x][!r],child[x][!r]=y;
if(z!=0) child[z][dir(y)]=x;
if(child[y][r]!=0) fa[child[y][r]]=y;
fa[y]=x,fa[x]=z,pushup(y),pushup(x);
return;
}
void splay(int &z,int x){ // 伸展
for(int y;(y=fa[x])!=0;rotate(x))
if(fa[y]!=0) rotate(dir(x)==dir(y)?y:x);
z=x;
return;
}
void find(int &z,int v){ // 按值查找
int x=z,y=fa[x];
while(x!=0&&val[x]!=v) x=child[y=x][v>val[x]];
splay(z,x!=0?x:y);
return;
}
void loc(int &z,int k){ // 按排名访问
int x=z;
while(true){
if(siz[child[x][0]]>=k) x=child[x][0];
else if(siz[child[x][0]]+cnt[x]>=k) break;
else k-=siz[child[x][0]]+cnt[x],x=child[x][1];
}
splay(z,x);
return;
}
int merge(int x,int y){ // 合并两颗树
if(x==0||y==0) return x|y;
loc(y,1),child[y][0]=x,fa[x]=y;
pushup(y);
return y;
}
void insert(int v){ // 插入
int x=root,y=0;
while(x!=0&&val[x]!=v) x=child[y=x][v>val[x]];
if(x!=0) cnt[x]++;
else{
x=++id,val[x]=v,cnt[x]=siz[x]=1,fa[x]=y;
if(y!=0) child[y][v>val[y]]=x;
}
splay(root,x);
return;
}
bool remove(int v){ // 删除
find(root,v);
if(root==0||val[root]!=v) return false;
cnt[root]--,siz[root]--;
if(cnt[root]==0){
int x=child[root][0],y=child[root][1];
fa[x]=fa[y]=0;
root=merge(x,y);
}
return true;
}
int findrank(int v){ //查找节点排名
find(root,v);
return siz[child[root][0]]+(val[root]<v?cnt[root]:0)+1;
}
int findval(int k){ // 查找节点值
if(k>siz[root]) return -1;
loc(root,k);
return val[root];
}
int findpre(int v){ // 查找前驱
if(root==0) return -1;
find(root,v);
if(root!=0&&val[root]<v) return val[root];
int x=child[root][0];
if(x==0) return -1;
while(child[x][1]!=0) x=child[x][1];
splay(root,x);
return val[root];
}
int findnxt(int v){ // 查找后缀
if(root==0) return -1;
find(root,v);
if(root!=0&&val[root]>v) return val[root];
int x=child[root][1];
if(x==0) return -1;
while(child[x][0]!=0) x=child[x][0];
splay(root,x);
return val[root];
}
int main(){
int n;
scanf("%d",&n);
while(n--){
int opt,x;
scanf("%d%d",&opt,&x);
if(opt==1) insert(x);
else if(opt==2) remove(x);
else if(opt==3) printf("%d\n",findrank(x));
else if(opt==4) printf("%d\n",findval(x));
else if(opt==5) printf("%d\n",findpre(x));
else printf("%d\n",findnxt(x));
}
return 0;
}
原理懒得写了,因为要写 \(Splay\) 就需要写平衡树定义,要写平衡树就又要写二叉搜索树,要写二叉搜索树就会惊奇地发现树方面的东西几乎没写过。遂弃之。
有需要的人可以对着查错(但还请不要复制粘贴)。

浙公网安备 33010602011771号