FHQ Treap 学习笔记
FHQ Treap 的基本概念与地位
首先,FHQ Treap(以下简称为 FHQ)是一种无旋 Treap,它主要通过分裂与合并操作来维持其本身的平衡性。
因此,FHQ 可以完成 Splay,Treap以及 bst 所支持的一切操作,非常的好用。
它同普通的 Treap 一样,都是依靠随机数来维持它本身的平衡。
并且由于 FHQ 短小的代码,深受机房大佬们的喜爱。
而且它似乎并不需要任何前置知识。
FHQ Treap 的几个基本操作
split(now,k,x,y)
这一个操作负责将 \(now\) 节点的子树按照权值或排名分裂成两棵平衡树 \(x,y\)。
由于笔者太弱,暂时只学了以权值分裂,所以暂不提供按排名分裂的代码。
主要流程如下:
- 判断节点 \(now\) 的权值是否大于 \(k\),如果是,进入第 \(2\) 步,否则进入第 \(3\) 步,如果已经没有节点可以划分,则返回;
- 由于 \(now\) 节点的权值大于 \(k\) 按照平衡树的性质,以其右子树的所有节点都大于 \(k\),所以我们将 \(now\) 节点及其右子树划分给 \(y\) 树,再递归进入左子树返回第 \(1\) 步判断;
- 由于 \(now\) 节点的权值小于等于 \(k\) 所以其左子树的所有节点都小于等于 \(k\),所以我们将 \(now\) 节点及其左子树划分给 \(x\) 树,再递归进入右子树返回第 \(1\) 步判断;
在分裂过程中由于节点的子树可能会发生变化,所以需要在回溯的过程中更新节点信息。
void split(int now,int k,int &x,int &y){
if(!now) x=y=0;//判断是否还有节点
else{
if(val[now]<=k)
x=now,split(ch[now][1],k,ch[now][1],y);//如上述
else
y=now,split(ch[now][0],k,x,ch[now][0]);
update(now);
}
}
最坏情况下一路分裂到底,时间复杂度一般来说是 \(\mathcal{O}(\log_2 n)\)。
merge(x,y)
合并操作与分裂操作是 FHQ 最重要的两个操作。
顾名思义,merge(x,y) 就是将 \(x,y\) 两棵平衡树合并为一颗平衡树。
为了维持平衡树的性质,所以我们在合并时以节点的随机值 \(pri[]\) 来划分。
这个函数们什么好讲的,直接看注释吧。
int merge(int x,int y){
if(!x||!y) return x+y;//一棵空树与一棵树合并后的结果就是这棵树
if(pri[x]<pri[y]){
ch[x][1]=merge(ch[x][1],y);//按照性质,将 y 树合并到 x 的右子树
update(x);//合并过程中子树发生了变化,在回溯的过程中更新节点信息
return x;
}
else{
ch[y][0]=merge(x,ch[y][0]);
update(y);
return y;
}
}
最坏情况下一路穿到底,时间复杂度一般为 \(\mathcal{O}(\log_2 n)\)。
插入一个数 \(a\)
这个很简单,只要将整棵树按 \(a\) 分裂成两棵树,再把 \(a\) 合并进去就行了。
split(rt,a,x,y);
rt=merge(merge(x,newnode(a)),y);
FHQ Treap 的几道例题
P6136 【模板】普通平衡树(数据加强版)
一道平衡树板题,按题意模拟即可。
Code:
#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;
const int N(1e6+1e5+5);
int n,m;
int ch[N][2],val[N],pri[N],siz[N],sz;
inline void update(int now){
siz[now]=siz[ch[now][0]]+1+siz[ch[now][1]];
}
inline int newnode(int v){
siz[++sz]=1;
val[sz]=v;
pri[sz]=rand();
return sz;
}
void split(int now,int k,int &x,int &y){
if(!now) x=y=0;
else{
if(val[now]<=k)
x=now,split(ch[now][1],k,ch[now][1],y);
else
y=now,split(ch[now][0],k,x,ch[now][0]);
update(now);
}
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(pri[x]<pri[y]){
ch[x][1]=merge(ch[x][1],y);
update(x);
return x;
}
else{
ch[y][0]=merge(x,ch[y][0]);
update(y);
return y;
}
}
inline int kth(int now,int k){
while(114514){
if(k<=siz[ch[now][0]])
now=ch[now][0];
else if(k==siz[ch[now][0]]+1)
return now;
else
k-=siz[ch[now][0]]+1,now=ch[now][1];
}
}
int ans,last;
int main(){
#ifdef ytxy
freopen("in.txt","r",stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
srand(time(NULL));
int rt=0,x,y,z;
cin>>n>>m;
for(register int i=1;i<=n;i++){
int a;cin>>a;
split(rt,a,x,y);
rt=merge(merge(x,newnode(a)),y);
}
while(m--){
int op,a;
cin>>op>>a;
a^=last;
if(op==1){
split(rt,a,x,y);
rt=merge(merge(x,newnode(a)),y);
}
else if(op==2){
split(rt,a,x,z);
split(x,a-1,x,y);
y=merge(ch[y][0],ch[y][1]);
rt=merge(merge(x,y),z);
}
else if(op==3){
split(rt,a-1,x,y);
last=siz[x]+1;
ans^=siz[x]+1;
rt=merge(x,y);
}
else if(op==4){
last=val[kth(rt,a)];
ans^=last;
}
else if(op==5){
split(rt,a-1,x,y);
last=val[kth(x,siz[x])];
ans^=last;
rt=merge(x,y);
}
else if(op==6){
split(rt,a,x,y);
last=val[kth(y,1)];
ans^=last;
rt=merge(x,y);
}
}
cout<<ans;
}
P2343 宝石管理系统
很简单的一道题,只要支持查询第 \(k\) 大,以及插入一个数即可。
先将 \(m\) 个数插入 \(FHQ Treap\),再按题意执行 \(q\) 次操作。
Code:
#include<iostream>
#include<cstdlib>
using namespace std;
const int N(1e5+3e4+5);
int m,q;
int siz[N],val[N],pri[N],ch[N][2],sz;
inline void update(int now){
siz[now]=siz[ch[now][0]]+1+siz[ch[now][1]];
}
inline int newnode(int v){
val[++sz]=v;
siz[sz]=1;
pri[sz]=rand();
return sz;
}
void split(int now,int k,int &x,int &y){
if(!now) x=y=0;
else{
if(val[now]<=k)
x=now,split(ch[now][1],k,ch[now][1],y);
else
y=now,split(ch[now][0],k,x,ch[now][0]);
update(now);
}
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(pri[x]<pri[y]){
ch[x][1]=merge(ch[x][1],y);
update(x);
return x;
}
else{
ch[y][0]=merge(x,ch[y][0]);
update(y);
return y;
}
}
int kth(int now,int k){
while(1919810){
if(k<=siz[ch[now][0]])
now=ch[now][0];
else if(k==siz[ch[now][0]]+1)
return now;
else
k-=siz[ch[now][0]]+1,now=ch[now][1];
}
}
int main(){
#ifdef ytxy
freopen("in.txt","r",stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>m>>q;
int rt=0,x,y,cnt=m;
for(int i=1;i<=m;i++){
int a;cin>>a;
split(rt,a,x,y);
rt=merge(merge(x,newnode(a)),y);
}
while(q--){
int c,n;
cin>>c>>n;
if(c==1){
cout<<val[kth(rt,cnt-n+1)]<<"\n"[q==0];
}
else if(c==2){
split(rt,n,x,y);
rt=merge(merge(x,newnode(n)),y);
cnt++;
}
}
}

浙公网安备 33010602011771号