fhqTreap 学习笔记

前言

首先 sto fhq orz。

以前有一篇【笔记】平衡树,但是只讲了 BST 和 Splay(还没有讲全)。今天补一补 神·fhq 发明的 fhqTreap。

关于 fhqTreap

fhqTreap 又称为 无旋Treap。顾名思义,就是 “不用旋转的Treap”。

fhqTreap 仅仅使用 分裂(split) 和 合并(merge)来支持其他平衡树拥有的操作。

fhqTreap 详解

操作数据 \(\verb! (data)!\)

...
const int N=1e5+10;
...
	private:
		#define lc(rt) son[rt][0]
		#define rc(rt) son[rt][1]
		int cnt=0;
		int a[N], sz[N], rk[N], son[N][2];
        ...
	public:
		int root=0;
		...
...

\(\verb!def: lc(rt) :=son[rt][0]!\):左儿子。

\(\verb!def: rc(rt) :=son[rt][1]!\):右儿子。

\(\verb!var: cnt !\):结点数。

\(\verb!arr: a[N] !\):结点的值。

\(\verb!arr: sz[N] !\):以结点为根的子树大小。

\(\verb!arr: rk[N] !\):结点的优先级。

\(\verb!var: root !\):……

新建结点 操作 \(\verb! (node)!\)

根据定义自行理解。

int node(int val){
	sz[++cnt]=1;
	a[cnt]=val;
	rnk[cnt]=rand();
	return cnt;
}

合并 操作 \(\verb! (merge)!\)

\(\verb!merge!\) 操作就是合并一下根节点和新建节点 然后保留根/替换根(\(rk\) 决定)

反正是随机合并 期望树高 \(\log n\)

int merge(int x, int y){
	if(!x||!y) return x|y;//空树
    //根据 rk 决定
	if(rnk[x]<rnk[y]){
		rc(x)=merge(rc(x),y);
		pushup(x);
		return x;
	}
	else{
		lc(y)=merge(x,lc(y));
		pushup(y);
		return y;
	}
}

分裂 操作 \(\verb! (split)!\)

\(\verb!split!\) 实现的操作大概是 把一棵树分成俩…然后左边的值小于等于 \(val\) 右边的值大于 \(val\)

如果你 split(rt,val,x,y)
那么你就把 \(rt\) 分成两个部分 \(x,y\) 了 其中 \(x\) 的子树的 \(v_x\leq val\)\(y\) 的子树的 \(v_y>k\)

void split(int rt, int val, int& x, int& y){
	if(!rt){x=y=0;return;}//空
    //根据 val 决定
	if(a[rt]<=val){
		x=rt;
		split(rc(x),val,rc(x),y);
	}
	else{
		y=rt;
		split(lc(y),val,x,lc(y));
	}
	pushup(rt);//记得更新
}

查找第 \(k\)\(\verb! (kth)!\)

可以按照 \(sz\) 来找 \(\operatorname{kth}\)。 递归/非递归都行。

int kth(int rt, int k){
	if(k<=sz[lc(rt)]) return kth(lc(rt),k);
	if(sz[lc(rt)]+1==k) return a[rt];
	return kth(rc(rt),k-sz[lc(rt)]-1);
}

查找排名为 \(x\) 的数 \(\verb! (rnk)!\)

\(\verb!rank!\) 的话就分离一个 \(val=k-1\) 的子树。然后求这个子树的 \(sz\) 就知道 \(\verb!rank!\) 了。

int rnk(int val){
	int x, y, res;
	split(root,val-1,x,y);
	res=sz[x]+1;
	root=merge(x,y);
	return res;
}

其他操作

\(\verb!rnk!\) 差不多,不讲了。

代码实现

\[\verb!Class !\operatorname{fhq}. \]

class fhq{
	private:
		#define lc(rt) son[rt][0]
		#define rc(rt) son[rt][1]
		int cnt=0;
		int a[N], sz[N], rnk[N], son[N][2];
		//新建结点
		int node(int val){
			sz[++cnt]=1;
			a[cnt]=val;
			rnk[cnt]=rand();
			return cnt;
		}
		void pushup(int rt){
		sz[rt]=sz[lc(rt)]+sz[rc(rt)]+1;}
		//按照BST的性质合并
		int merge(int x, int y){
			if(!x||!y) return x|y;
			if(rnk[x]<rnk[y]){
				rc(x)=merge(rc(x),y);
				pushup(x);
				return x;
			}
			else{
				lc(y)=merge(x,lc(y));
				pushup(y);
				return y;
			}
		}
		//将rt分裂成x和y (其中vx<=val,vy>val)
		void split(int rt, int val,
			int& x, int& y){
			if(!rt){x=y=0;return;}
			if(a[rt]<=val){
				x=rt;
				split(rc(x),val,rc(x),y);
			}
			else{
				y=rt;
				split(lc(y),val,x,lc(y));
			}
			pushup(rt);
		}
	public:
		int root=0;
		//插入一个点
		void insert(int val){
			int x, y;
			split(root,val,x,y);
			root=merge(merge(x,node(val)),y);
		}
		void erase(int val){
			int x, y, z;
			split(root,val,x,z);
			split(x,val-1,x,y);
			y=merge(lc(y),rc(y));
			root=merge(merge(x,y),z);
		}
		int kth(int rt, int k){
			if(k<=sz[lc(rt)])
				return kth(lc(rt),k);
			if(sz[lc(rt)]+1==k) return a[rt];
			return kth(rc(rt),k-sz[lc(rt)]-1);
		}
		int ranks(int val){
			int x, y, res;
			split(root,val-1,x,y);
			res=sz[x]+1;
			root=merge(x,y);
			return res;
		}
		int pre(int val){
			int x, y, res;
			split(root,val-1,x,y);
			res=kth(x,sz[x]);
			root=merge(x,y);
			return res;
		}
		int suf(int val){
			int x, y, res;
			split(root,val,x,y);
			res=kth(y,1);
			root=merge(x,y);
			return res;
		}
}t;

练习题:【模板】普通平衡树

posted @ 2022-01-21 20:34  int32  阅读(45)  评论(0)    收藏  举报