FHQ-Treap 学习笔记


前置知识:带旋Treap
虽然但是,FHQ-Treap居然没有带旋的Treap快。

FHQ-Treap 不同于普通的 Treap,FHQ-Treap 不需要通过旋转来执行操作,而是通过两个核心操作:分裂与合并。

节点的存储的信息和初始化还是和原来一样。

struct JTZ{
	int ls,rs,siz,val,f;
}tr[maxn]; int K,root,T,opt,x;
int newnode(int x){ K++; tr[K].siz=1; tr[K].val=x; tr[K].f=rand(); return K; }
void up(int x){ tr[x].siz=tr[tr[x].ls].siz+tr[tr[x].rs].siz+1; return; }
void init(){
	newnode(-INF); newnode(INF); tr[1].rs=2; up(2); up(1);
	if(tr[1].f>tr[2].f) mswap(tr[1].f,tr[2].f); root=1; return;
}

分裂

如果有一棵 Treap,对应的值域为 \([l,r]\),考虑把这棵 Treap 分裂成 \([l,k]\)\([k+1,r]\) 两个部分。

分裂的时候,如果当前根在左半部分左半部分的根就是当前节点,在右半部分右半部分的根就是当前节点,然后递归左右子树即可。分裂的时候因为原来的父子关系就是合法的,所以不需要考虑堆的性质。
最后记得 pushup 当前根节点。

void split(int p,int k,int &x,int &y){//代表当前根为 p,分裂的中间值 k 分裂成的左右子树为 x,y
	if(!p){ x=y=0; return; }
	if(tr[p].val<=k){ x=p; split(tr[p].rs,k,tr[p].rs,y); }
	else { y=p; split(tr[p].ls,k,x,tr[p].ls); } up(p); return;
}

合并

合并就是分裂的逆过程,注意合并要求两个子树对应的区间 \([a,b],[c,d]\) 满足 \(b\le c\)
显然如果有一个为空就不需要合并。
考虑左右两个根节点的堆的值。
考虑维护堆性质,如果左边的比右边的大,那么左边的根就合并完的根,然后接下去递归合并左边根的右儿子为根的子树和右边的子树,合并完放在左边根的右儿子;如果右边的比左边大也是一样的。
最后还是要记得 pushup 合并完之后的根节点。

int merge(int x,int y){//代表合并左边的根为 x 右边的根为 y 返回值为合并完自会后的根节点
	if(!x||!y) return x|y;
	if(tr[x].f<tr[y].f){ tr[x].rs=merge(tr[x].rs,y); up(x); return x; }
	else { tr[y].ls=merge(x,tr[y].ls); up(y); return y; }
}

其他细节

有了这两个操作,我们就可以完成 Luogu P3369 【模板】普通平衡树 中的操作了。
插入 \(x\):把原来的树分裂成 \([-\infin,x]\)\([x+1,\infin]\) 两段,然后把原来的两部分以及新加入的一个 \(x\) 节点这三部分合并起来就好了。
删除 \(x\):把树分裂成 \([-\infin,x-1],[x,x],[x+1,\infin]\) 三个部分,然后把 \([x,x]\) 这一段的根节点的左右儿子合并成为新的 \([x,x]\) 这一段的子树,然后合并这三段。
其他的操作是一样的。

代码:

#include<ctime>
#include<cstdio>
#include<cstdlib>
#define db double
#define gc getchar
#define pc putchar
#define U unsigned
#define ll long long
#define ld long double
#define ull unsigned long long
#define Tp template<typename _T>
#define Me(a,b) memset(a,b,sizeof(a))
Tp _T mabs(_T a){ return a>0?a:-a; }
Tp _T mmax(_T a,_T b){ return a>b?a:b; }
Tp _T mmin(_T a,_T b){ return a<b?a:b; }
Tp void mswap(_T &a,_T &b){ _T tmp=a; a=b; b=tmp; return; }
Tp void print(_T x){ if(x<0) pc('-'),x=-x; if(x>9) print(x/10); pc((x%10)+48); return; }
#define EPS (1e-7)
#define INF (0x7fffffff)
#define LL_INF (0x7fffffffffffffff)
#define maxn 100039
#define maxm
#define MOD
#define Type int
#ifndef ONLINE_JUDGE
#define debug
#endif
using namespace std;
Type read(){
	char c=gc(); Type s=0; int flag=0;
	while((c<'0'||c>'9')&&c!='-') c=gc(); if(c=='-') c=gc(),flag=1;
	while('0'<=c&&c<='9'){ s=(s<<1)+(s<<3)+(c^48); c=gc(); }
	if(flag) return -s; return s;
}
struct JTZ{
	int ls,rs,siz,val,f;
}tr[maxn]; int K,root,T,opt,x;
int newnode(int x){ K++; tr[K].siz=1; tr[K].val=x; tr[K].f=rand(); return K; }
void up(int x){ tr[x].siz=tr[tr[x].ls].siz+tr[tr[x].rs].siz+1; return; }
void init(){
	newnode(-INF); newnode(INF); tr[1].rs=2; up(2); up(1);
	if(tr[1].f>tr[2].f) mswap(tr[1].f,tr[2].f); root=1; return;
}
void split(int p,int k,int &x,int &y){
	if(!p){ x=y=0; return; }
	if(tr[p].val<=k){ x=p; split(tr[p].rs,k,tr[p].rs,y); }
	else { y=p; split(tr[p].ls,k,x,tr[p].ls); } up(p); return;
}
int merge(int x,int y){
	if(!x||!y) return x|y;
	if(tr[x].f<tr[y].f){ tr[x].rs=merge(tr[x].rs,y); up(x); return x; }
	else { tr[y].ls=merge(x,tr[y].ls); up(y); return y; }
}
void insert(int k){ int x,y; split(root,k,x,y); root=merge(merge(x,newnode(k)),y); return; }
void del(int k){
	int x,y,z; split(root,k,x,z); split(x,k-1,x,y);
	y=merge(tr[y].ls,tr[y].rs); root=merge(merge(x,y),z); return;
}
int findrk(int p,int x){
	if(!p) return 0;
	if(x<=tr[p].val) return findrk(tr[p].ls,x);
	else return tr[tr[p].ls].siz+1+findrk(tr[p].rs,x);
}
int findval(int p,int x){
	if(x==tr[tr[p].ls].siz+1) return tr[p].val;
	else if(x<=tr[tr[p].ls].siz) return findval(tr[p].ls,x);
	else return findval(tr[p].rs,x-tr[tr[p].ls].siz-1);
}
int findpre(int p,int x){
	if(!p) return -INF;
	if(tr[p].val>=x) return findpre(tr[p].ls,x);
	else return mmax(tr[p].val,findpre(tr[p].rs,x));
}
int findnex(int p,int x){
	if(!p) return INF;
	if(tr[p].val<=x) return findnex(tr[p].rs,x);
	else return mmin(tr[p].val,findnex(tr[p].ls,x));
}
int main(){
	srand(time(0)); init(); T=read(); while(T--){
		opt=read(); x=read();
		if(opt==1) insert(x); else if(opt==2) del(x);
		else if(opt==3) print(findrk(root,x)),pc('\n');
		else if(opt==4) print(findval(root,x+1)),pc('\n');
		else if(opt==5) print(findpre(root,x)),pc('\n');
		else print(findnex(root,x)),pc('\n');
	}
	return 0;
}
posted @ 2022-08-27 19:37  jiangtaizhe001  阅读(49)  评论(0编辑  收藏  举报