FHQ Treap

P3369 【模板】普通平衡树

前言:

平衡树是一种二叉搜索树,通过一些方法来做到快速维护单点或区间信息和快速查询单点或区间信息,其中包括排名、前驱等等。在 \(\rm STL\) 库中虽有实现,但是由于封装的太好以及是可持久化数据结构的基础,还是需要学习的。

FHQ Treap:

FHQ Treap 是一种不带旋操作的平衡树,使用分裂和合并为基础操作,以此来维护树的平衡性。

节点操作:

节点定义:

这没啥好讲的。

struct node{
	int ls,rs;//左右儿子
	int val,key;// 值与键值
	int siz;// 子树大小
}fhq[N];
int root,cnt;// 根节点编号和节点数量

新建节点:

inline int newnode(int val){// 返回新节点编号
	fhq[++cnt].val=val;
	fhq[cnt].key=rnd();//这里用的是 std::mt19937 rnd(114514)
	fhq[cnt].siz=1;
	return cnt;
}

更新节点信息:

inline void update(int o){
	fhq[o].siz=fhq[fhq[o].ls].siz+fhq[fhq[o].rs].siz+1;
}

基础操作:

分裂(split):

将树以阈值 \(val\) 拆分成 \(\le val\)\(> val\) 的两棵树。

inline void split(int now,int val,int& x,int& y){// now 当前节点,val 是阈值,x 是 <=val 的树,y 是 >val 的树(都取根节点来表示)
	if(!now)x=y=0;//空树
	else{
		if(fhq[now].val<=val){// 假如当前节点的值小于等于阈值,则左子树中的所有节点都是 x 树 的,右子树则需要递归判断
			x=now;
			split(fhq[now].rs,val,fhq[now].rs,y);
		}
		else{// 否则此时的右子树都属于 y 树,左子树递归判断
			y=now;
			split(fhq[now].ls,val,x,fhq[now].ls);
		}
		update(now);// 记得更新节点信息
	}
}

合并(merge):

合并两棵树为一棵树,而且前面的树一定要是在左边的。

inline int merge(int x,int y){// 返回根节点编号
	if(!x||!y)return x+y;// 假如两树有一树为空或是两树都为空,则返回其中不空的一棵或是返回空
	if(fhq[x].key>fhq[y].key){// 假如 x 树根节点的键值大于 y 树根节点的,此时 x 树根节点一定在 y 树根节点上方。
        			  //也就是 x 树根节点就是合并后两棵树的根节点,所以此时让右子树再去跟 y 树合并。
		fhq[x].rs=merge(fhq[x].rs,y);
		update(x);// 更新节点信息
		return x;
	}
	else{// 否则同理
		fhq[y].ls=merge(x,fhq[y].ls);
		update(y);
		return y;
	}
}

更新操作:

插入(insert):

插入一个值为 \(val\) 的新节点。

int x,y,z;
inline void ins(int val){
	split(root,val,x,y);//先将原树以 val 为阈值割分成两部分 x,y
	root=merge(merge(x,newnode(val)),y);//此时我们按 x,new,y 的顺序合并即可
}

删除(delete):

删除一个值为 \(val\) 的节点。

inline void del(int val){
	split(root,val,x,z);// 先将原树树割分成 <=val(x) 和 >val(z)
	split(x,val-1,x,y);// 再将 x 树割分成 <val(x) 和 =val(y) 
	y=merge(fhq[y].ls,fhq[y].rs);//将 y 树中的根节点删除
	root=merge(merge(x,y),z);// 合并回来
}

查询一个数的排名(getrank):

查询一个数 \(val\) 在序列中是第几大的数。

inline int getrank(int val){
	split(root,val-1,x,y);//把 val-1 的部分割出来
	int rank=fhq[x].siz+1;// 此时直接求就好
	root=merge(x,y);// 合并回来
	return rank;
}

查询排名所对应的值(getnum):

查询排名 \(rank\) 所对应的值。

inline int getnum(int rank){
	int now=root;
	while(now){
		if(fhq[fhq[now].ls].siz+1==rank)break;// 假如这个节点就是,直接 break 掉
		if(fhq[fhq[now].ls].siz<rank){// 左子树的节点不够
			rank-=(fhq[fhq[now].ls].siz+1);//减掉左子树的节点数量,即为需要在右子树中找第几大的数
			now=fhq[now].rs;// 往右走
		}
		else now=fhq[now].ls;// 左子树节点数量还绰绰有余时,就往左子树走
	}
	return fhq[now].val;
}

查询一个数的前驱(pre):

查询在比值 \(val\) 小 的数集合中最大的数。

inline int pre(int val){
	split(root,val-1,x,y);// 把树割成 <val(x) 和 >=val(y) 两树
	int now=x;
	while(fhq[now].rs)now=fhq[now].rs;// 在 x 树中最大的数就是最右边的数
	root=merge(x,y);// 合并回来
	return fhq[now].val;
}

查询一个数的后继(nxt):

查询在比值 \(val\) 大的数集合中最小的数。

inline int nxt(int val){// 同理
	split(root,val,x,y);
	int now=y;
	while(fhq[now].ls)now=fhq[now].ls;
	root=merge(x,y);
	return fhq[now].val;
}

至此我们顺利的完成了所有操作。

code:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e5+10;
std::mt19937 rnd(114514);
struct FHQ_Treap{
	struct node{
		int ls,rs;
		int val,key;
		int siz;
	}fhq[N];
	int root,cnt;
	inline int newnode(int val){
		fhq[++cnt].val=val;
		fhq[cnt].key=rnd();
		fhq[cnt].siz=1;
		return cnt;
	}
	inline void update(int o){
		fhq[o].siz=fhq[fhq[o].ls].siz+fhq[fhq[o].rs].siz+1;
	}
	inline void split(int now,int val,int& x,int& y){
		if(!now)x=y=0;
		else{
			if(fhq[now].val<=val){
				x=now;
				split(fhq[now].rs,val,fhq[now].rs,y);
			}
			else{
				y=now;
				split(fhq[now].ls,val,x,fhq[now].ls);
			}
			update(now);
		}
	}
	inline int merge(int x,int y){
		if(!x||!y)return x+y;
		if(fhq[x].key>fhq[y].key){
			fhq[x].rs=merge(fhq[x].rs,y);
			update(x);
			return x;
		}
		else{
			fhq[y].ls=merge(x,fhq[y].ls);
			update(y);
			return y;
		}
	}
	int x,y,z;
	inline void ins(int val){
		split(root,val,x,y);
		root=merge(merge(x,newnode(val)),y);
	}
	inline void del(int val){
		split(root,val,x,z);
		split(x,val-1,x,y);
		y=merge(fhq[y].ls,fhq[y].rs);
		root=merge(merge(x,y),z);
	}
	inline int getrank(int val){
		split(root,val-1,x,y);
		int rank=fhq[x].siz+1;
		root=merge(x,y);
		return rank;
	}
	inline int getnum(int rank){
		int now=root;
		while(now){
			if(fhq[fhq[now].ls].siz+1==rank)break;
			if(fhq[fhq[now].ls].siz<rank){
				rank-=(fhq[fhq[now].ls].siz+1);
				now=fhq[now].rs;
			}
			else now=fhq[now].ls;
		}
		return fhq[now].val;
	}
	inline int pre(int val){
		split(root,val-1,x,y);
		int now=x;
		while(fhq[now].rs)now=fhq[now].rs;
		root=merge(x,y);
		return fhq[now].val;
	}
	inline int nxt(int val){
		split(root,val,x,y);
		int now=y;
		while(fhq[now].ls)now=fhq[now].ls;
		root=merge(x,y);
		return fhq[now].val;
	}
}tree;

signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--){
		int opt,val;
		cin>>opt>>val;
		if(opt==1)tree.ins(val);
		if(opt==2)tree.del(val);
		if(opt==3)cout<<tree.getrank(val)<<"\n";
		if(opt==4)cout<<tree.getnum(val)<<"\n";
		if(opt==5)cout<<tree.pre(val)<<"\n";
		if(opt==6)cout<<tree.nxt(val)<<"\n";
	}

	return 0;
}
posted @ 2024-04-25 13:15  Little_corn  阅读(6)  评论(0编辑  收藏  举报