fhq_treap
数据结构 fhq_treap
各种阴差阳错地得知了这个数据结构,于是学习了一波
fhq_treap,非旋treap,据说是一位叫范浩强的人发明的。该treap厉害在虽然思路类似于treap,但无需旋转操作。可以在经可能维持空间形态(方便可持久化改造)的同时保证时空复杂度
fhq_treap的各种操作总结下来归功于两个基本操作:分裂(split)和合并(merge)
分裂分为两类,权值分裂和大小分裂。注意:两者虽然写法极其类似,但各自有不同的用途
分裂
权值分裂:(平衡树的话用这个!)
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]);}
updata(now);
}
}
大小分裂:
void split(int now,int k,int &x,int &y){
if(!now) x=y=0;
else{
int t=size[ch[now][0]]+1;
if(t<=k) {x=now; split(ch[now][1],k-t,ch[now][1],y);}
else {y=now; split(ch[now][0],k,x,ch[now][0]);}
updata(now);
}
}
初看的时候不好理解,画图看看思索就好了
先忽略到两个难以理解的引用,先看now和k:这无非就是now在不断挑来挑去对吧,保证now在满足k的同时,经可能出现在边界(大概是这个意思)
再尝试理解一下引用:

个人感觉把自己的见解比较形象地提炼出来了
合并
int merge(int a,int b){
if(!a||!b) return a+b;
if(rad[a]<rad[b]) {ch[a][1]=merge(ch[a][1],b); updata(a); return a;}
else {ch[b][0]=merge(a,ch[b][0]); updata(b); return b;}
}
合并没什么好说的。注意ch[a][0]和ch[b][1]都已经排好序了,故无需再次合并
剩下地就是用这两个操作去实现各种功能:
插入:
void insert(int k){
split(root,k,x,y);
root=merge(merge(x,newnode(k)),y);
}
删除:
void delate(int k){
split(root,k,x,z);
split(x,k-1,x,y);
y=merge(ch[y][0],ch[y][1]);
root=merge(merge(x,y),z);
}
查询x的排名:
int rnk(int k){
split(root,k-1,x,y);
int ans=size[x]+1;
root=merge(x,y);
return ans;
}
查询排名为x的数字:
int kth(int pos,int k){
int now=pos;
while(now){
if(size[ch[now][0]]+1==k) return now;
if(k<=size[ch[now][0]]) now=ch[now][0];
else k-=size[ch[now][0]]+1,now=ch[now][1];
}
}
查询x的前驱:
int pre(int k){
split(root,k-1,x,y);
int ans=val[kth(x,size[x])];
root=merge(x,y);
return ans;
}
查询x的后驱:
int suf(int k){
split(root,k,x,y);
int ans=val[kth(y,1)];
root=merge(x,y);
return ans;
}
运用好分裂和合并,平衡树也可以告别难受的旋转ヾ(≧▽≦*)o
练习:【模板】普通平衡树
完整代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define FT faQ_treap::
inline int read();
namespace faQ_treap{
const int MAX=1e5+5;
int tot,root;
int val[MAX],ch[MAX][2],size[MAX],rad[MAX],x,y,z;
void updata(int k){
size[k]=size[ch[k][0]]+size[ch[k][1]]+1;
}
int newnode(int k){
++tot;
val[tot]=k;
size[tot]=1;
rad[tot]=rand();
return tot;
}
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]);}
updata(now);
}
}
int merge(int a,int b){
if(!a||!b) return a+b;
if(rad[a]<rad[b]) {ch[a][1]=merge(ch[a][1],b); updata(a); return a;}
else {ch[b][0]=merge(a,ch[b][0]); updata(b); return b;}
}
void insert(int k){
split(root,k,x,y);
root=merge(merge(x,newnode(k)),y);
}
void delate(int k){
split(root,k,x,z);
split(x,k-1,x,y);
y=merge(ch[y][0],ch[y][1]);
root=merge(merge(x,y),z);
}
int rnk(int k){
split(root,k-1,x,y);
int ans=size[x]+1;
root=merge(x,y);
return ans;
}
int kth(int pos,int k){
int now=pos;
while(now){
if(size[ch[now][0]]+1==k) return now;
if(k<=size[ch[now][0]]) now=ch[now][0];
else k-=size[ch[now][0]]+1,now=ch[now][1];
}
}
int pre(int k){
split(root,k-1,x,y);
int ans=val[kth(x,size[x])];
root=merge(x,y);
return ans;
}
int suf(int k){
split(root,k,x,y);
int ans=val[kth(y,1)];
root=merge(x,y);
return ans;
}
bool check(int k){
int u=root;
while(u){
if(val[u]==k) return true;
if(val[u]>k) u=ch[u][0];
else u=ch[u][1];
}
return false;
}
}
int n,type,k;
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
n=read();
for(int i=1;i<=n;++i){
type=read(); k=read();
switch(type){
case 1:
FT insert(k);
break;
case 2:
FT delate(k);
break;
case 3:
printf("%d\n",FT rnk(k));
break;
case 4:
printf("%d\n",FT val[FT kth(FT root,k)]);
break;
case 5:
printf("%d\n",FT pre(k));
break;
case 6:
printf("%d\n",FT suf(k));
break;
}
}
return 0;
}
inline int read(){
char tmp=getchar(); int sum=0; bool flag=false;
while(tmp<'0'||tmp>'9'){
if(tmp=='-') flag=true;
tmp=getchar();
}
while(tmp>='0'&&tmp<='9'){
sum=(sum<<1)+(sum<<3)+tmp-'0';
tmp=getchar();
}
return flag?-sum:sum;
}
fhq_treap的权值分裂应用于平衡树的实现,可以完成平衡树可以完成的全部内容。
fhq_的大小分裂主要应用于解决区间问题。具体请参考我的博客上"题解 [NOI2005]维护数列"

浙公网安备 33010602011771号