FHQ Treap 1.有旋Treap​

一些数组定义

int sum,R,num[N],sz[N],son[N][2],rd[N];

s u m sum sum结点数

R R R根结点编号

n u m [ ] num[] num[]当前结点出现的次数(与该结点值相同的个数)

s z [ ] sz[] sz[]该结点的子树大小

s o n [ ] [ ] son[][] son[][]记录结点的左右儿子

r d [ ] rd[] rd[]该结点的随机值,用于维护二叉堆 H e a p Heap Heap.

向上合并更新父亲信息

il void re(int p){
	sz[p]=sz[son[p][0]]+sz[son[p][1]]+num[p];
}

旋转维护Heap

il void rot(int &p,int d){
	int k=son[p][d^1]; //左旋取儿子,右选取左儿子.
	son[p][d^1]=son[p][d]; //更新根结点的左儿子右为k的左儿子
	son[k][d]=p;//k的左儿子设为p
	re(p),re(k),p=k;更新p,k信息,根更新为k.
}
  • 插入
  • 删除
  • 根据值找排名
  • 根据排名找值
  • 前驱
  • 后继

基本操作

void ins(int &p,int x){
	if(!p){ //为空结点直接新建
		p=++sum,sz[p]=num[p]=1;
		v[p]=x,rd[p]=rand();return;
	}
	if(v[p]==x){ //重复 更新num,sz
		num[p]++,sz[p]++;return;
	}
	int d=x>v[p]; //往子树插入
	ins(son[p][d],x);  
	if(rd[p]<rd[son[p][d]]) rot(p,d^1); //维护Max Heap
	re(p);
}
void del(int &p,int x){
	if(!p) return; //为空直接返回
	if(x!=v[p]) del(son[p][x>v[p]],x); //往子树找
	else { 
		if(!son[p][0]&&!son[p][1]){ //叶子直接更新
			num[p]--,sz[p]--;
			if(!num[p]) p=0;  //为空删除 p=0
		}
		else if(son[p][0]&&!son[p][1]){ //右旋找右子树
			rot(p,1);
			del(son[p][1],x);
		}
		else if(!son[p][0]&&son[p][1]){ //左旋找左子树
			rot(p,0);
			del(son[p][0],x);
		}
		else { //找到rd较大的往该子树找
			int d=rd[son[p][0]]>rd[son[p][1]];
			rot(p,d);del(son[p][d],x);
		}
	}
	re(p);
}
int rk(int p,int x){
    if (!p) return 0;
    if (v[p]==x) return sz[son[p][0]]+1;
    if (v[p]<x) return sz[son[p][0]]+num[p]+rk(son[p][1],x);
    if (v[p]>x) return rk(son[p][0],x);
}
int find(int p,int x){
    if (!p) return 0;
    if (sz[son[p][0]]>=x) return find(son[p][0],x);
    else if (sz[son[p][0]]+num[p]<x)
        return find(son[p][1],x-num[p]-sz[son[p][0]]);
    else return v[p];
}
int pre(int p,int x){
    if (!p) return -inf;
    if (v[p]>=x) return pre(son[p][0],x);
    else return max(v[p],pre(son[p][1],x));
}
int suf(int p,int x){
    if (!p) return inf;
    if (v[p]<=x) return suf(son[p][1],x);
    else return min(v[p],suf(son[p][0],x));
}

完整版

#define il inline
int sum,R,num[N],sz[N],son[N][2],rd[N],v[N];
il void re(int p){
	sz[p]=sz[son[p][0]]+sz[son[p][1]]+num[p];
}
il void rot(int &p,int d){
	int k=son[p][d^1];
	son[p][d^1]=son[k][d];
	son[k][d]=p;
	re(p),re(k),p=k;
}
void ins(int &p,int x){
	if(!p){
		p=++sum,sz[p]=num[p]=1;
		v[p]=x,rd[p]=rand();return;
	}
	if(v[p]==x){
		num[p]++,sz[p]++;return;
	}
	int d=x>v[p];
	ins(son[p][d],x);
	if(rd[p]<rd[son[p][d]]) rot(p,d^1);
	re(p);
}
void del(int &p,int x){
	if(!p) return;
	if(x!=v[p]) del(son[p][x>v[p]],x);
	else {
		if(!son[p][0]&&!son[p][1]){
			num[p]--,sz[p]--;
			if(!num[p]) p=0; 
		}
		else if(son[p][0]&&!son[p][1]){
			rot(p,1);
			del(son[p][1],x);
		}
		else if(!son[p][0]&&son[p][1]){
			rot(p,0);
			del(son[p][0],x);
		}
		else {
			int d=rd[son[p][0]]>rd[son[p][1]];
			rot(p,d);del(son[p][d],x);
		}
	}
	re(p);
}
int rk(int p,int x){
    if (!p) return 0;
    if (v[p]==x) return sz[son[p][0]]+1;
    if (v[p]<x) return sz[son[p][0]]+num[p]+rk(son[p][1],x);
    if (v[p]>x) return rk(son[p][0],x);
}
int find(int p,int x){
    if (!p) return 0;
    if (sz[son[p][0]]>=x) return find(son[p][0],x);
    else if (sz[son[p][0]]+num[p]<x)
        return find(son[p][1],x-num[p]-sz[son[p][0]]);
    else return v[p];
}
int pre(int p,int x){
    if (!p) return -inf;
    if (v[p]>=x) return pre(son[p][0],x);
    else return max(v[p],pre(son[p][1],x));
}
int suf(int p,int x){
    if (!p) return inf;
    if (v[p]<=x) return suf(son[p][1],x);
    else return min(v[p],suf(son[p][0],x));
}
2.无旋FHQ Treap

按权值分裂

int R; //全局定义初始化为0
int w[N],rd[N],sz[N],son[N][2],cnt; //定义在结构体内

w [ ] w[] w[]结点权值

r d [ ] rd[] rd[]结点的修正值,维护堆

s z [ ] sz[] sz[]结点子树大小

s o n [ ] [ ] son[][] son[][]记录左右儿子

c n t cnt cnt结点总数

向上合并信息

inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}

创建新结点

inline int newnode(int x){w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}

分裂

	void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;} //如果i为空 左右子树=0 返回
           //w[i]<=k 把i丢进左子树 左子树的虚拟结点为右儿子,递归
		if(w[i]<=k) x=i,split(son[i][1],k,son[i][1],y);
        //否则i丢进右子树,右子树的虚拟节点为左儿子,递归                     
		else y=i,split(son[i][0],k,x,son[i][0]);
		re(i);
	}

合并

	int merge(int x,int y){if(!x||!y) return x+y; //有一个空值即返回另一个子树
                           //左子树小把右儿子和右子树合并.
		if(rd[x]<rd[y]) //修正值小的作为根.
			{son[x][1]=merge(son[x][1],y);re(x);return x;}
                          //右子树小把左儿子和左子树合并.
		son[y][0]=merge(x,son[y][0]);re(y);return y;
	}
  • 插入
  • 删除
  • 值找排名
  • 排名找值
  • 前驱
  • 后继

基本操作

	void ins(int a){		int x,y;		split(R,a,x,y);		R=merge(merge(x,newnode(a)),y);	}	void del(int a){		int x,y,z;		split(R,a,x,z);		split(x,a-1,x,y);		y=merge(son[y][0],son[y][1]);		R=merge(merge(x,y),z);	} 	int kth(int x,int k){		while(1){			if(k<=sz[son[x][0]]) x=son[x][0];			else if(k==sz[son[x][0]]+1) return w[x];			else k-=sz[son[x][0]]+1,x=son[x][1];		}	}	int find(int a){		int x,y;		split(R,a-1,x,y);		int ans=sz[x]+1;		R=merge(x,y);		return ans;	}	int pre(int a){		int x,y;		split(R,a-1,x,y);		int ans=kth(x,sz[x]);		R=merge(x,y);		return ans;	}	int suf(int a){		int x,y;		split(R,a,x,y);		int ans=kth(y,1);		R=merge(x,y);		return ans;	}

完整版

int R;
struct Treap{ 
	int w[N],rd[N],sz[N],son[N][2],cnt;
	inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}
	inline int newnode(int x){w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
	int merge(int x,int y){if(!x||!y) return x+y;
		if(rd[x]<rd[y])
			{son[x][1]=merge(son[x][1],y);re(x);return x;}
		son[y][0]=merge(x,son[y][0]);re(y);return y;
	}
	void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;}
		if(w[i]<=k) x=i,split(son[i][1],k,son[i][1],y);
		else y=i,split(son[i][0],k,x,son[i][0]);
		re(i);
	}
	void ins(int a){
		int x,y;
		split(R,a,x,y);
		R=merge(merge(x,newnode(a)),y);
	}
	void del(int a){
		int x,y,z;
		split(R,a,x,z);
		split(x,a-1,x,y);
		y=merge(son[y][0],son[y][1]);
		R=merge(merge(x,y),z);
	} 
	int kth(int x,int k){
		while(1){
			if(k<=sz[son[x][0]]) x=son[x][0];
			else if(k==sz[son[x][0]]+1) return w[x];
			else k-=sz[son[x][0]]+1,x=son[x][1];
		}
	}
	int find(int a){
		int x,y;
		split(R,a-1,x,y);
		int ans=sz[x]+1;
		R=merge(x,y);
		return ans;
	}
	int pre(int a){
		int x,y;
		split(R,a-1,x,y);
		int ans=kth(x,sz[x]);
		R=merge(x,y);
		return ans;
	}
	int suf(int a){
		int x,y;
		split(R,a,x,y);
		int ans=kth(y,1);
		R=merge(x,y);
		return ans;
	}
}T;

按size分裂

无论怎么旋转,每一个 k e y [ u ] key[u] key[u]的下标对应 s i z e [ l s o n ] + 1 size[lson]+1 size[lson]+1

这样中序遍历就是从下标 1 1 1 n n n的。

k e y [ u ] key[u] key[u]就是原数组下标为 s i z e [ l s o n ] + 1 size[lson]+1 size[lson]+1的值。

  • 该数在原数组中的下标= s i z e [ l s o n ] + 1 size[lson]+1 size[lson]+1
struct Treap{ //fhq treap (split by rank)
	int w[N],rd[N],sz[N],son[N][2],cnt;
	bool lz[N];
	inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;} //向上合并信息
	inline int ins(int x) 
        //插入结点 
    {w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
	void down(int x){//do something
		if(lz[x]){ //
			swap(son[x][0],son[x][1]);
			lz[son[x][0]]^=1;
			lz[son[x][1]]^=1;
			lz[x]=0;
		}}
	int merge(int x,int y){if(!x||!y) return x+y;down(x),down(y);
		if(rd[x]<rd[y])
			{son[x][1]=merge(son[x][1],y);re(x);return x;}
		son[y][0]=merge(x,son[y][0]);re(y);return y;
	}
	void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;}down(i);
		if(sz[son[i][0]]<k) x=i,split(son[i][1],k-sz[son[i][0]]-1,son[i][1],y);
		else y=i,split(son[i][0],k,x,son[i][0]);
		re(i);
	}
	void fun(int x){ //inorder traversal
		if(!x) return;down(x);
		fun(son[x][0]);printf("%d ",w[x]);fun(son[x][1]);
	}
}T;
int n,m,R=0;
例题

P3369 【模板】普通平衡树

有旋Treap和FHQ-VAL-Treap 都可。

P6136 【模板】普通平衡树(数据加强版)

同上。

P2343 宝石管理系统

插入+第 k k k大操作,同上。

P1503 鬼子进村

FHQ-VAL-Treap

用栈维护恢复操作。

房子被炸了就插入

询问就是后继-前驱-1,注意用一个数组标记该房子是否被炸,如果被炸了询问的答案为0。

先加入哨兵结点 0 , n + 1 0,n+1 0,n+1

// Problem: P1503 鬼子进村
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1503
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-02-26 12:17:32
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=5e4+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\n",a[n]); 
}
int R;
struct Treap{ //fhq treap (split by rank wtihout rotation)
	int w[N],rd[N],sz[N],son[N][2],cnt;
	bool lz[N];
	inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}
	inline int newnode(int x){w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
	void ins(int a){
		int x,y;
		split(R,a,x,y);
		R=merge(merge(x,newnode(a)),y);
	}
	void del(int a){
		int x,y,z;
		split(R,a,x,z);
		split(x,a-1,x,y);
		y=merge(son[y][0],son[y][1]);
		R=merge(merge(x,y),z);
	}
	void down(int x){//do something
		if(lz[x]){
			swap(son[x][0],son[x][1]);
			lz[son[x][0]]^=1;
			lz[son[x][1]]^=1;
			lz[x]=0;
		}}
	int merge(int x,int y){if(!x||!y) return x+y;
		if(rd[x]<rd[y])
			{son[x][1]=merge(son[x][1],y);re(x);return x;}
		son[y][0]=merge(x,son[y][0]);re(y);return y;
	}
	void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;}
		/*(if split by value) 
		modify: if(w[i]<=k) x=i,split(son[i][1],k,son[i][1],y);
		*/	
		if(w[i]<=k) x=i,split(son[i][1],k,son[i][1],y);
		else y=i,split(son[i][0],k,x,son[i][0]);
		re(i);
	}
	//(if split by value) 
	int kth(int x,int k){
		while(1){
			if(k<=sz[son[x][0]]) x=son[x][0];
			else if(k==sz[son[x][0]]+1) return w[x];
			else k-=sz[son[x][0]]+1,x=son[x][1];
		}
	}
	int pre(int a){
		int x,y;
		split(R,a-1,x,y);
		int ans=kth(x,sz[x]);
		R=merge(x,y);
		return ans;
	}
	int suf(int a){
		int x,y;
		split(R,a,x,y);
		int ans=kth(y,1);
		R=merge(x,y);
		return ans;
	}
	void fun(int x){ //inorder traversal
		if(!x) return;down(x);
		fun(son[x][0]);printf("%d ",w[x]);fun(son[x][1]);
	}
}T;
int vis[N];
int main(){
	int n,m;scanf("%d%d",&n,&m);
	T.ins(0),T.ins(n+1);
	stack<int>st;
	while(m--){
		char op[3];int x;
		scanf("%s",op);
		if(op[0]=='D'){
			scanf("%d",&x);T.ins(x);st.push(x);
			vis[x]=1;
		}
		else if(op[0]=='R'){
			if(!st.empty()){
				x=st.top();
				T.del(x);vis[x]=0;
				st.pop();
			}
		}
		else {
			scanf("%d",&x);
			if(vis[x]) printf("0\n");
			else printf("%d\n",T.suf(x)-T.pre(x)-1);
		}
	}
	return 0;
}

P1533 可怜的狗狗

FHQ Treap

区间排序,然后离线处理,双指针插入删除,这样可以实现区间第 k k k大了。

	for(int i=1;i<=m;i++) scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k),q[i].id=i;
	sort(q+1,q+m+1);
	int L=1,RR=0;
	for(int i=1;i<=m;i++){
		int l=q[i].l,r=q[i].r,k=q[i].k;
		while(RR<r) T.ins(a[++RR]);
		while(L<l) T.del(a[L++]);
		ans[q[i].id]=T.kth(R,k);
	}

P1801 黑匣子

插入+全局第 k k k大 sb fhqTreap


P2073 送花

支持 插入+删除+全局第 k k k

fhq treap 即可。

维护总美丽值和价格,用两个变量即可。

因为最后的花的价格都不同,所以用一个map维护是否重复即可。

即价格唯一映射美丽值。

较水。

	while(~scanf("%d",&op)&&~op){
		if(op==1){
			int w,c;scanf("%d%d",&w,&c);
			if(mp[c]) continue;
			mp[c]=w,T.ins(c);++tot;sum+=w;res+=c;
		}
		else if(op==2){ 
			if(!tot) continue;
			int w=T.kth(R,tot);
			T.del(w);tot--;sum-=mp[w];mp[w]=0;res-=w;
		}
		else if(op==3){
			if(!tot) continue;
			int w=T.kth(R,1);
			T.del(w);tot--;sum-=mp[w];mp[w]=0;res-=w;
		}
	}
	printf("%lld %lld\n",sum,res);

按SIZE分裂的习题

区间翻转 P3391 【模板】文艺平衡树

区间翻转等价于该子树的左右儿子结点向下交换。

首先按照 l − 1 l-1 l1分裂,得到 [ 1 , l − 1 ] [1,l-1] [1,l1] [ l , n ] [l,n] [l,n]

然后再从 [ l , n ] [l,n] [l,n]分裂出size大小为 r − l + 1 r-l+1 rl+1的,注意是按 s i z e size size分裂的,所以选出前 r − l + 1 r-l+1 rl+1个就对应的该区间。

然后用线段树思想 懒标记处理一下就行了。

// Problem: P3391 【模板】文艺平衡树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3391
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-07-19 18:25:05
// --------by Herio--------

// Problem: P3391 【模板】文艺平衡树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3391
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-02-25 23:20:48
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\n",a[n]); 
}
struct Treap{ //fhq treap (split by rank)
	int w[N],rd[N],sz[N],son[N][2],cnt;
	bool lz[N];
	inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}
	inline int ins(int x){w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
	void down(int x){//do something
		if(lz[x]){
			swap(son[x][0],son[x][1]);
			lz[son[x][0]]^=1;
			lz[son[x][1]]^=1;
			lz[x]=0;
		}}
	int merge(int x,int y){if(!x||!y) return x+y;down(x),down(y);
		if(rd[x]<rd[y])
			{son[x][1]=merge(son[x][1],y);re(x);return x;}
		son[y][0]=merge(x,son[y][0]);re(y);return y;
	}
	void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;}down(i);
		if(sz[son[i][0]]<k) x=i,split(son[i][1],k-sz[son[i][0]]-1,son[i][1],y);
		else y=i,split(son[i][0],k,x,son[i][0]);
		re(i);
	}
	void fun(int x){ //inorder traversal
		if(!x) return;down(x);
		fun(son[x][0]);printf("%d ",w[x]);fun(son[x][1]);
	}
}T;
int n,m,R=0;
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline T& read(T& r) {
    r = 0; bool w = 0; char ch = getchar();
    while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
    while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
    return r = w ? -r : r;
}
int main(){
	read(n),read(m);
	for(int i=1;i<=n;i++) R=T.merge(R,T.ins(i));
	while(m--){
		int l,r,x,y,z;read(l),read(r);
		T.split(R,l-1,x,y);
		T.split(y,r-l+1,y,z);
		T.lz[y]^=1;
		R=T.merge(x,T.merge(y,z));
	}
	T.fun(R);
	return 0;
}