一、简介
fhq treap 与一般的treap主要有3点不同
1、不用旋转
2、以merge和split为核心操作,通过它们的组合实现平衡树的所有操作
3、可以可持久化
二、核心操作
代码中val表示节点权值,pri表示节点的优先级,维护小根堆
1、split
将1个treap分裂为两个treap
分裂主要有两种:以权值k作为分界点、以位置k作为分界点
①以权值k作为分界点
设原来的treap根节点为root,分裂后的<=k的treap A 的根节点为x,>k的treap B 的根节点为y
当前节点指针now从root开始,在treap A 的x位置加点,treap B 的y位置加点
若now的权值<=k,那么以now为根的左子树全部包含在A中,
把now给x,去now的右子树,x变成x的右子树,y还是y
若now的权值>k,那么以now为分的右子树全部包含在B中,
把now给y,去now的左子树,x还是x,y变成y的左子树
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); }}
②以位置k作为分界点
设原来的treap根节点为root,分裂后的前k个节点的treap A 的根节点为x,剩下的节点的treap B 的根节点为y
判断放在哪颗treap的标准改成k和子树大小的比较,原理同上
void split(int now,int k,int &x,int &y){ if(!now) x=y=0; else { if(k<=siz[ch[now][0]]) { y=now; split(ch[now][0],k,x,ch[now][0]); } else { x=now; split(ch[now][1],k-siz[ch[now][0]]-1,ch[now][1],y); } update(now); }}
2、merge
合并以x为根的treap A 和 以y为根的treap B
前提:A的权值全部<B的权值
注意在merge的过程中,维护好堆和二叉搜索树的性质
若优先级x<y,
维护堆的性质-->y成为x的子节点
维护二叉搜索树的性质-->y在x的右子树
若优先级x>y,
维护堆的性质-->x成为y的子节点
维护二叉搜索树的性质-->x在y的左子树
int merge(int x,int y){ if(!x || !y) return x+y; if(pri[x]
三、单点操作
1、建树
建树有两种方式:nlogn 一个一个插、n 直接建
这里主要说后者
每次取mid作为当前子树的根节点即可
int new_node(int v){ siz[++tot]=1; val[tot]=v; pri[tot]=rand(); return tot;}int build(int l,int r){ if(l>r) return 0; int mid=l+r>>1,v=val[mid]; int now=new_node(v); ch[now][0]=build(l,mid-1); ch[now][1]=build(mid+1,r); update(now); return now;}
2、插入
添加权值为v的点
1、按权值以v为分界点,split成两棵treapA(<=v),B( >v )
2、合并A和v,得到C
3、合并C和B
merge要求x的权值<y决定了 合并的顺序
split(root,v,x,y);root=merge(merge(x,new_node(v)),y);
3、删除
删除一个权值为v的点
1、按权值以v为分界点,split成两棵treap A(<=v),B( >v)
2、把A按权值以v-1为分界点,split成两棵treap C(<=v-1) ,D(=v)
3、合并D的根节点的左右子树,相当于删除根节点,得到treap E
4、合并C和E,得F
5、合并F和B
split(root,v,x,z);split(x,v-1,x,y);y=merge(ch[y][0],ch[y][1]);root=merge(merge(x,y),z);
4、查询v的排名
1、按权值以v-1为分界点,split成两棵treap A(<=v-1) B(>=v)
2、A的大小+1 即为权值v的排名
3、合并A、B
split(root,v-1,x,y);cout<<<'\n';root=merge(x,y);
5、查询排名为x的数
int get_kth(int now,int k){ while(1) { if(k<=siz[ch[now][0]]) now=ch[now][0]; else { k-=siz[ch[now][0]]; if(k==1) return now; k--; now=ch[now][1]; } }}cout<<<'\n';
6、查找比v小的最大的数(前驱)
1、按权值以v-1为分界点,split成两棵treap A(<=v-1),B(>=v)
2、在A中查找A的排名最靠后的数,输出
3、合并A和B
split(root,v-1,x,y);cout<<<'\n';root=merge(x,y);
7、查询比v大的最小的数(后继)
1、按权值以v为分界点,split成两棵treap A(<=v),B(>v)
2、在B中找最靠前的数,输出
3、合并A和B
split(root,v,x,y);cout<<<'\n';root=merge(x,y);
四、区间操作
对区间[l,r]执行某种操作
1、以位置r作为分界点,split成两棵treap A(前r个节点)、B(位置r之后的节点)
2、在A中以位置l-1为分界点,split成两棵treap C(前l-1个节点)、D(第l到第r个节点)
3、对D执行操作
注意因为执行区间操作,增加2个虚拟节点,最前面一个,最后面一个,所有节点后移一位
加虚拟节点的原因是 如果对区间[1,x]或[x,n]操作,那我们需要用到1-1 号 点和n+1号店
split(root,r+1,a,b);split(a,l,c,d);work(d)
五、模板
1、普通平衡树
#include#include #include #include using namespace std;#define N 100001int root;int tot;int fa[N],ch[N][2],val[N],pri[N],siz[N];void read(int &x){ x=0; int f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } x*=f;}void update(int x){ siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;}int new_node(int v){ siz[++tot]=1; val[tot]=v; pri[tot]=rand(); return tot;}int merge(int x,int y){ if(!x || !y) return x+y; if(pri[x]
2、文艺平衡树
#include#include #include #include using namespace std;#define N 100005int n;int tot,root;int siz[N],ch[N][2],val[N],pri[N];bool rev[N];void read(int &x){ x=0; int f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } x*=f;}int new_node(int v){ siz[++tot]=1; val[tot]=v; pri[tot]=rand(); return tot;}void update(int x){ siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];}int build(int l,int r){ if(l>r) return 0; int mid=l+r>>1,v=mid-1; int now=new_node(v); ch[now][0]=build(l,mid-1); ch[now][1]=build(mid+1,r); update(now); return now;}void down(int x){ rev[x]^=1; swap(ch[x][0],ch[x][1]); rev[ch[x][0]]^=1; rev[ch[x][1]]^=1;}int merge(int x,int y){ if(!x || !y) return x+y; if(rev[x]) down(x); if(rev[y]) down(y); if(pri[x] =1 && val[x]<=n) cout< <<' '; if(ch[x][1]) dfs(ch[x][1]);}int main(){ srand(time(0)+20001024); int m; read(n); read(m); root=build(1,n+2); int l,r; while(m--) { read(l); read(r); reverse(l,r); } dfs(root);}