FHQ-Treap 学习笔记
範浩強之木,無旋之奇構,併合眸,妙用無窮;其當官也,避繁複之旋。其視心有二,分若離,合若聚,若星漢分合變幻,肖無跡矣。不用旋,巧避繁,古之所未有,今之所獨異。茲樹形奇,如天成,真算之妙。---------《算枢奇构》
### 基本操作
众所周知,无旋treap不需要旋转,基本操作有两个,分裂(Split)和合并(Merge)。
分离:指的是将一棵Treap按照中序遍历的顺序,分割成左右两半,满足左右两半组成的Treap的所有值都不变。
合并:指的是将两棵Treap(一般是从原先的TreapSplit出来的)合并在一起,按照中序遍历的顺序,并且所有节点的值都不变。
Split
Split操作比较简单,先讲讲如何实现:当然,Split之前要先指定一个值k,表示Split出这个Treap的中序遍历中的前k个数作为第一棵Split出的Treap。
从这个Treap的根开始,看它的左子树的大小是否大于等于k,如果是,那么说明右子树和根都在第二棵中,继续递归到左子树中。
如果不是,那么说明左子树和根都在第一棵Treap中,继续递归到右子树中,而且k要减去左子树的大小加一。
void Split(int rt,int k,int&rt1,int&rt2){
if(!rt) {rt1=rt2=0; return;}
if(k<=siz[ls[rt]]){
Split(ls[rt],k,rt1,rt2);
ls[rt]=rt2;
combine(rt);
rt2=rt;
}
else{
Split(rs[rt],k-siz[ls[rt]]-1,rt1,rt2);
rs[rt]=rt1;
combine(rt);
rt1=rt;
}
}
Merge
考虑两个根节点的pri值,因为第一棵在第二棵前面,所以要不然rt1(第一棵的根)在rt2(第二棵的根)的左子树,要不然rt2在rt1的右子树。
但是因为有了pri的影响,所以只能rt1和rt2中pri较大的那个作为根。
如果rt1为根,那么有rt1的右子树和rt2合并作为rt1的现在的右子树。
如果rt2为根,那么有rt2的左子树和rt1合并作为rt2的现在的左子树。
两种情况都递归进子树中即可。
int Merge(int rt1,int rt2){
if(!rt1) return rt2;
if(!rt2) return rt1;
if(pri[rt1]<pri[rt2]){
rs[rt1]=Merge(rs[rt1],rt2);
combine(rt1);
return rt1;
}
else{
ls[rt2]=Merge(rt1,ls[rt2]);
combine(rt2);
return rt2;
}
}
查询排名
以当前值分裂,小的那颗树的siz就是排名
插入节点
按值分裂为两颗树,将要插入的值插在中间
删除节点
按值x和x-1拆成三棵树,将只有一个节点(也就是要删的那个)甩了,只合并另外两棵树
查询第K个值
和删除很像,分裂成三棵树,输出中间的那个节点再合并起来
区间操作
由于可以很方便的分离区间,所以结合懒标记技巧可以非常方便的操作区间,就是被忘了pushdown
模板代码
#include<bits/stdc++.h>
#define N 200005
using namespace std;
inline int read(){
int x=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x*w;
}
int t,n,m,cnt,root;
bool flag[N];
struct node{int l,r,val,key,siz;}tree[N];
void push_up(int p){
tree[p].siz=tree[tree[p].l].siz+1+tree[tree[p].r].siz;
}
int build_new(int x){
tree[++cnt].siz=1;
tree[cnt].val=x;
tree[cnt].key=rand();
return cnt;
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(tree[x].key<tree[y].key){
tree[x].r=merge(tree[x].r,y);
push_up(x);
return x;
}
else{
tree[y].l=merge(x,tree[y].l);
push_up(y);
return y;
}
}
void split(int p,int k,int &x,int &y){
if(!p) {
x=y=0;
return;
}
if(tree[p].val<=k) x=p,split(tree[p].r,k,tree[p].r,y);
else y=p,split(tree[p].l,k,x,tree[p].l);
push_up(p);
}
int kth(int p,int k){//返回的是节点编号
if(k<=tree[tree[p].l].siz) return kth(tree[p].l,k);
else if(k==tree[tree[p].l].siz+1) return p;
else return kth(tree[p].r,k-tree[tree[p].l].siz-1);
}
signed main(){
srand((unsigned)time(NULL));
t=read();
while(t--){
int opt=read(),x=read();
if(opt==1) {
int a,b;
split(root,x,a,b);
root=merge(a,merge(build_new(x),b));
}
else if(opt==2) {
int a,b,c;
split(root,x,a,b);
split(a,x-1,a,c);
c=merge(tree[c].l,tree[c].r);
root=merge(merge(a,c),b);
}
else if(opt==3){
int a,b;
split(root,x-1,a,b);
printf("%d\n",tree[a].siz+1);
root=merge(a,b);
}
else if(opt==4) printf("%d\n",tree[kth(root,x)].val);
else if(opt==5){
int a,b;
split(root,x-1,a,b);
printf("%d\n",tree[kth(a,tree[a].siz)].val);
root=merge(a,b);
}
else{
int a,b;
split(root,x,a,b);
printf("%d\n",tree[kth(b,1)].val);
root=merge(a,b);
}
}
return 0;
}