(简记)FHQ Treap

补题总是发现不想写旋转 Treap,今天终于重学了一遍 FHQ Treap 大法,特此总结(并不)。

FHQ Treap

FHQ Treap 通过不断的分裂和合并操作维护一个 BST(一个 BST 是一颗二叉树,每个节点具有优先级 \(\text{pri}\) 和键值 \(\text{key}\),其中键值要满足 \(t[ls].key\le t[p].key \le t[rs].key\),优先级满足大根堆性质即 \(t[p].pri\geq t[ls].pri,t[p].pri\geq t[rs].pri\))。

基本操作

Split

分裂指将以 \(p\) 为根节点的 BST 分为根为 \(L,R\) 的两棵 BST,使得 \(L\) 树上所有点的键值都 \(\le x\)\(R\) 树上的所有点键值都 \(>x\)

分裂成两棵树的操作函数十分简单:

void Split(int p,int x,int &L,int &R){
	if(!p){L=R=0;return ;}
	if(t[p].key<=x){
		L=p;
		Split(t[p].rs,x,t[p].rs,R);
	}
	else {
		R=p;
		Split(t[p].ls,x,L,t[p].ls);
	}
	pushup(p);
}

代码中引用传递的L,R分别代表以 \(p\) 为根的树分裂后得到的左树和右树的根。分类讨论即可。注意每层函数的L,R的意义不尽相同,需要分开考虑。

这里的L,R分别指代分出的左右两棵树需要接上的节点。

Merge

int Merge(int L,int R){
	if(!L||!R)return L|R;
	if(t[L].pri>t[R].pri){
		t[L].rs=Merge(t[L].rs,R);
		pushup(L);
		return L;
	}
	else {
		t[R].ls=Merge(L,t[R].ls);
		pushup(R);
		return R;
	}
}

同样的,把两棵树并起来的过程,考虑每层的节点谁优先值更大就当父亲,根据优先顺序把另一个点接上去,可以想象理想状态下两棵形态完全相同的树一个节点变成两个节点(这样的比喻好像不太合适,但只是方便理解),然后把递归建树的问题扔给儿子。

时间分析

通过上述代码我们可以看出,由于 BST 的期望高度为 \(O(\log n)\),且上述操作每一层都是 \(O(1)\) 的,我们有很好的理由相信一次操作的总时间就是 \(O(\log n)\) 的。

  • BST 的期望高度为 \(\Theta(\log n)\)

  • 随机普通二叉树的期望高度为 \(\Theta(\sqrt n)\),其证明转移至OI-wiki:关于 Catalan 数

普通树:

例题

针对一般平衡树维护,我们在每个节点多维护一个表示子树大小的 size,然后简单加以灵活应用即可。由于这是一篇简记而不是笔记,就不详细展开了。针对一些操作,我们可以每次以 \(x-1\)\(x\) 为 Split 分界点然后拆成两棵树,接着利用 BST 的查找(kth)等完成普通平衡树所需操作,需要注意的是每次操作完要把两棵树并起来。

下面给出P3369 【模板】普通平衡树的 FHQ Treap 完整代码。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int ncnt,root,n;
struct Node{
	int ls,rs;
	int key,pri;
	int size;
}t[N];
inline int newNode(int x){
	ncnt++;
	t[ncnt].key=x;
	t[ncnt].pri=rand();
	t[ncnt].size=1;
	return ncnt;
}
inline void pushup(int p){
	t[p].size=1+t[t[p].ls].size+t[t[p].rs].size;
}
void Split(int p,int x,int &L,int &R){
	if(!p){L=R=0;return ;}
	if(t[p].key<=x){
		L=p;
		Split(t[p].rs,x,t[p].rs,R);
	}
	else {
		R=p;
		Split(t[p].ls,x,L,t[p].ls);
	}
	pushup(p);
}
int Merge(int L,int R){
	if(!L||!R)return L|R;
	if(t[L].pri>t[R].pri){
		t[L].rs=Merge(t[L].rs,R);
		pushup(L);
		return L;
	}
	else {
		t[R].ls=Merge(L,t[R].ls);
		pushup(R);
		return R;
	}
}
void Insert(int x){
	int L,R;
	Split(root,x,L,R);
	root=Merge(Merge(L,newNode(x)),R);
}
void Delete(int x){
	int L,R,p;
	Split(root,x,L,R);
	Split(L,x-1,L,p);
	p=Merge(t[p].ls,t[p].rs);
	root=Merge(Merge(L,p),R);
}
int Rank(int x){
	int L,R,p,res;
	Split(root,x-1,L,R);
	res=t[L].size+1;
	Merge(L,R);
	return res;
}
int kth(int p,int k){
	if(t[t[p].ls].size+1==k)return t[p].key;
	if(k<=t[t[p].ls].size)return kth(t[p].ls,k);
	return kth(t[p].rs,k-t[t[p].ls].size-1);
}
int pre(int x){
	int L,R,res;
	Split(root,x-1,L,R);
	res=kth(L,t[L].size);
	Merge(L,R);
	return res;
}
int suc(int x){
	int L,R,res;
	Split(root,x,L,R);
	res=kth(R,1);
	Merge(L,R);
	return res;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	srand(time(NULL));
	cin>>n;
	for(int i=1;i<=n;i++){
		int opt,x;
		cin>>opt>>x;
		if(opt==1)Insert(x);
		else if(opt==2)Delete(x);
		else if(opt==3)cout<<Rank(x)<<'\n';
		else if(opt==4)cout<<kth(root,x)<<'\n';
		else if(opt==5)cout<<pre(x)<<'\n';
		else cout<<suc(x)<<'\n';
	}
	return 0;
}

可持久化版本

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int ncnt,root[N],n,ver,id;
struct Node{
	int ls,rs;
	int key,pri;
	int size;
}t[N*50];
inline int newNode(int x){
	ncnt++;
	t[ncnt].key=x;
	t[ncnt].pri=rand();
	t[ncnt].size=1;
	return ncnt;
}
inline void pushup(int p){
	t[p].size=1+t[t[p].ls].size+t[t[p].rs].size;
}
void Split(int p,int x,int &L,int &R){
	if(!p){L=R=0;return ;}
	if(t[p].key<=x){
		L=newNode(0);
		t[L]=t[p];
		Split(t[L].rs,x,t[L].rs,R);
		pushup(L);
	}
	else {
		R=newNode(0);
		t[R]=t[p];
		Split(t[R].ls,x,L,t[R].ls);
		pushup(R);
	}
}
int Merge(int L,int R){
	if(!L||!R)return L|R;
	if(t[L].pri>t[R].pri){
		int rt=newNode(0);
		t[rt]=t[L];
		t[rt].rs=Merge(t[rt].rs,R);
		pushup(rt);
		return rt;
	}
	else {
		int rt=newNode(0);
		t[rt]=t[R];
		t[rt].ls=Merge(L,t[rt].ls);
		pushup(rt);
		return rt;
	}
}
void Insert(int x){
	int L,R;
	Split(root[ver],x,L,R);
	root[id]=Merge(Merge(L,newNode(x)),R);
}
void Delete(int x){
	int L,R,p;
	Split(root[ver],x,L,R);
	Split(L,x-1,L,p);
	p=Merge(t[p].ls,t[p].rs);
	root[id]=Merge(Merge(L,p),R);
}
int Rank(int x){
	int L,R,p,res;
	Split(root[ver],x-1,L,R);
	res=t[L].size+1;
	root[id]=Merge(L,R);
	return res;
}
int kth(int p,int k){
	if(t[t[p].ls].size+1==k)return t[p].key;
	if(k<=t[t[p].ls].size)return kth(t[p].ls,k);
	return kth(t[p].rs,k-t[t[p].ls].size-1);
}
int pre(int x){
	int L,R,res;
	Split(root[ver],x-1,L,R);
	if(!L)return -(1ll<<31)+1;
	res=kth(L,t[L].size);
	root[id]=Merge(L,R);
	return res;
}
int suc(int x){
	int L,R,res;
	Split(root[ver],x,L,R);
	if(!R)return (1ll<<31)-1;
	res=kth(R,1);
	root[id]=Merge(L,R);
	return res;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	srand(time(NULL));
	cin>>n;
	for(id=1;id<=n;id++){
		int opt,x;
		cin>>ver>>opt>>x;
		if(opt==1)Insert(x);
		else if(opt==2)Delete(x);
		else if(opt==3)cout<<Rank(x)<<'\n';
		else if(opt==4)root[id]=root[ver],cout<<kth(root[ver],x)<<'\n';
		else if(opt==5)cout<<pre(x)<<'\n';
		else cout<<suc(x)<<'\n';
	}
	return 0;
}
posted @ 2025-04-23 14:31  TBSF_0207  阅读(23)  评论(0)    收藏  举报