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\) 就需要写平衡树定义,要写平衡树就又要写二叉搜索树,要写二叉搜索树就会惊奇地发现树方面的东西几乎没写过。遂弃之。

有需要的人可以对着查错(但还请不要复制粘贴)。

posted @ 2026-04-03 19:51  Rye-Whiskey  阅读(11)  评论(0)    收藏  举报