fhq-treap

基操

  • 一切splay支持的它都可以(要splay有何用),支持区间加,区间乘,找前驱后继,区间翻转等

  • 支持非序列上的操作(以需要排序的为键值)和序列(以中序遍历作为键值)

  • 序列可以直接建树,和笛卡尔树一样,时间\(O(n)\)

  • 可以可持久化(空间差不开50倍)

  • treap可以启发式合并(\(O(nlg^2n)\)),也可以利用split直接合并,时间复杂度更优,其中如果将\(n\)个大小为1的treap合并在一起时间复杂度\(O(nlgn)\)

    int Merge(int x,int y) {
    	if(!x||!y) return x|y;
        if(rnd[x]>rnd[y]) swap(x,y);
        int z,w; split(y,c[x],z,w);
        ls[x]=Merge(ls[x],z);
        rs[x]=Merge(rs[x],w);
        return x;
    }
    

模板1

Luogu P2234 [HNOI2002]营业额统计

set裸题!!!按权值split

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int cnt,c[N],mn[N],mx[N],rnd[N],n,rt,ls[N],rs[N],ans;

inline int New(int x) {
	++cnt; 
	c[cnt]=mn[cnt]=mx[cnt]=x; 
	rnd[cnt]=rand();
	return cnt;
}

inline void up(int p) {
	mx[p]=mn[p]=c[p];
	if(ls[p]) {
		mx[p]=max(mx[p],mx[ls[p]]);
		mn[p]=min(mn[p],mn[ls[p]]);
	}
	if(rs[p]) {
		mx[p]=max(mx[p],mx[rs[p]]);
		mn[p]=min(mn[p],mn[rs[p]]);
	}
}

void split(int p,int k,int &x,int &y) {
	if(!p) { 
		x=y=0; return;
	}
	if(c[p]<=k) {
		x=p; split(rs[p],k,rs[p],y);
	} else {
		y=p; split(ls[p],k,x,ls[p]);
	}
	up(p);
} 

int merge(int x,int y) {
	if(!x||!y) return x|y;
	if(rnd[x]<rnd[y]) {
		rs[x]=merge(rs[x],y);
		up(x);
		return x;
	}
	ls[y]=merge(x,ls[y]);
	up(y);
	return y;	 
}

int main() {
	scanf("%d",&n); 
	scanf("%d",&ans); 
	rt=New(ans);
	for(int i=2;i<=n;i++) {
		int t; scanf("%d",&t);
		int x=0,y=0;
		split(rt,t,x,y);
		int now=1e9;
		if(x) now=min(now,t-mx[x]);
		if(y) now=min(now,mn[y]-t);
		ans+=now;
		rt=merge(x,merge(New(t),y));
	}
	printf("%d\n",ans);
	return 0;
}

千万别再写a[++cnt]=b[cnt]这种脑瘫行为了

模板2

Luogu P3391 【模板】文艺平衡树

把元素当做键值,按照size分裂(显然)

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,m,cnt,c[N],rnd[N],ls[N],rs[N],r[N],sz[N],rt;

inline void up(int p) {
	sz[p]=sz[ls[p]]+sz[rs[p]]+1;
}
void bld(int &p,int l,int r) {
	if(l>r) {
		p=0; return;
	} 
	int mid=l+r>>1;
	p=++cnt; c[p]=mid,rnd[p]=rand();
	bld(ls[p],l,mid-1),bld(rs[p],mid+1,r);
	up(p);
}

inline void rev(int p) {
	swap(ls[p],rs[p]);
	r[p]^=1;
}
inline void down(int p) {
	if(r[p]) {
		if(ls[p]) rev(ls[p]);
		if(rs[p]) rev(rs[p]); 
		r[p]=0;
	}
}
void split(int p,int k,int &x,int &y) {
	if(!p) {
		x=y=0; return;
	}
	down(p);
	if(sz[ls[p]]+1<=k) {
		x=p; split(rs[p],k-sz[ls[p]]-1,rs[x],y);
	} else {
		y=p; split(ls[p],k,x,ls[y]);
	}
	up(p);
}

int merge(int x,int y) {
	if(!x||!y) return x|y;
	if(rnd[x]<rnd[y]) {
		down(x);
		rs[x]=merge(rs[x],y);
		up(x);
		return x;
	} else {
		down(y);
		ls[y]=merge(x,ls[y]);
		up(y);
		return y;
	}
}
void write(int p) {
	down(p);
	if(ls[p]) write(ls[p]);
	printf("%d ",c[p]);
	if(rs[p]) write(rs[p]);
}
int main(){
	scanf("%d%d",&n,&m);
	bld(rt,1,n);
	for(int i=1;i<=m;i++) {
		int u,v; scanf("%d%d",&u,&v);
		int x,y,z;
		split(rt,u-1,x,y);
		split(y,v-u+1,y,z);
		rev(y);
		rt=merge(x,merge(y,z));
	}
	write(rt);
	return 0;
}

Ps:裂开区间\([l,r]\)应该是split(rt,l-1,x,y),split(rt,r-l+1,y,z)

模板3

Luogu P3835 【模板】可持久化平衡树

split和merge时都新拷贝节点

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=5e5+5,M=3e7+5;
int cnt,c[M],rt[N],sz[M],rnd[M],ls[M],rs[M];

inline int New(int x) {
	c[++cnt]=x,sz[cnt]=1,rnd[cnt]=rand();
	return cnt;
}
inline int co(int p) {
	c[++cnt]=c[p],sz[cnt]=1,rnd[cnt]=rnd[p],ls[cnt]=ls[p],rs[cnt]=rs[p];
	return cnt;
}
inline void up(int p) {
	sz[p]=sz[ls[p]]+sz[rs[p]]+1;
}
void split(int p,int k,int &x,int &y) {
	if(!p) {
		x=y=0; return;
	}
	if(k>=c[p]) {
		x=co(p),split(rs[x],k,rs[x],y);
		up(x);
	} else {
		y=co(p),split(ls[y],k,x,ls[y]);
		up(y);
	}
}

int merge(int x,int y) {
	if(!x||!y) return x|y;
	if(rnd[x]<rnd[y]) {
		x=co(x);
		rs[x]=merge(rs[x],y);
		up(x);
		return x;
	}
	y=co(y);
	ls[y]=merge(x,ls[y]);
	up(y);
	return y;
}

inline int rnk(int p,int k) {
	int ret=1;
	while(p) {
		if(c[p]>=k) p=ls[p];
			else {
				ret+=sz[ls[p]]+1;
				p=rs[p];
			}
	}
	return ret;
}

inline int kth(int p,int k) {
	while(p) {
		if(sz[ls[p]]+1==k) {
			return c[p];
		}
		if(sz[ls[p]]>=k) p=ls[p];
			else {
				k-=sz[ls[p]]+1;
				p=rs[p];
			}
	}
}

inline int pre(int p,int x) {
	int ret=(-1LL<<31)+1;
	while(p) {
		if(c[p]<x) {
			ret=max(ret,c[p]);
			p=rs[p];
		} else p=ls[p];
	}
	return ret;
}

inline int suf(int p,int x) {
	int ret=(1LL<<31)-1;
	while(p) {
		if(c[p]>x) {
			ret=min(ret,c[p]);
			p=ls[p];
		} else p=rs[p];
	}
	return ret;
}

void write(int p) {
	if(ls[p]) write(ls[p]);
	printf("%d ",c[p]);
	if(rs[p]) write(rs[p]);
}
int main() {
	freopen("1.in","r",stdin); 
	int T; scanf("%d",&T); int x,y,z,t;
	for(int i=1;i<=T;i++) {
		int vi,op,t; scanf("%d%d%d",&vi,&op,&t);
		if(op==1) {
			split(rt[vi],t,x,y);
			rt[i]=merge(x,merge(New(t),y));
		} else if(op==2) {
			split(rt[vi],t-1,x,y);
			split(y,t,y,z);
			y=merge(ls[y],rs[y]);
			rt[i]=merge(x,merge(y,z));
		} else {
			rt[i]=rt[vi];
			if(op==3) {
				printf("%d\n",rnk(rt[i],t));
			} else if(op==4) {
				printf("%d\n",kth(rt[i],t));
			} else if(op==5) {
				printf("%d\n",pre(rt[i],t));
			} else {
				printf("%d\n",suf(rt[i],t));
			}
		}
	}
	return 0;
}

例题1

Luogu P2596 [ZJOI2006]书架

序列上的题,中序遍历为键值

唯一没法知道的是编号,可以记录父亲,将节点值作为编号,然后暴力往上跳,求出他的排名即可

树高为\(O(nlgn)\)

注意中途up时要把自己的fa赋值为0,防止出现无法修改根节点父亲的情况

建树和建笛卡尔树一样

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,m,ls[N],rs[N],fa[N],sz[N],rnd[N],c[N],rt;
char op[N];
inline void up(int p) {
	if(ls[p]) fa[ls[p]]=p;
	if(rs[p]) fa[rs[p]]=p;
	fa[p]=0;
	sz[p]=sz[ls[p]]+sz[rs[p]]+1;
}

void split(int p,int k,int &x,int &y) {
	if(!p) {
		x=y=0; return;
	}
	if(sz[ls[p]]<=k-1) {
		x=p; split(rs[x],k-sz[ls[p]]-1,rs[x],y);
	}  else {
		y=p; split(ls[y],k,x,ls[y]);
	}
	up(p);
}
int merge(int x,int y) {
	if(!x||!y) return x|y;
	if(rnd[x]<rnd[y]) {
		rs[x]=merge(rs[x],y);
		up(x);
		return x;
	} else{
		ls[y]=merge(x,ls[y]);
		up(y);
		return y;
	}
}

inline int find(int p) {
	int ret=sz[ls[p]]+1;
	for(;fa[p];p=fa[p]) {
		if(rs[fa[p]]==p) {
			ret+=sz[ls[fa[p]]]+1;
		}
	}
	return ret;
}
inline int New(int x) {
	sz[x]=1,rnd[x]=rand();
	return x;
}
int top,st[N];
int main(){
	scanf("%d%d",&n,&m); int top=0;
	for(int i=1;i<=n;i++) {
		scanf("%d",&c[i]);
		int u=New(c[i]),lst=0;;
		while(top&&rnd[st[top]]>rnd[u]) {
			lst=st[top]; up(st[top]); top--;
		}
		if(top) rs[st[top]]=u;
		ls[u]=lst;
		st[++top]=u;
	}
	for(;top;top--) up(st[top]);
	rt=st[1];
	int x,y,z;
	for(int i=1;i<=m;i++) {
		int t; scanf("%s%d",op,&t);
		if(op[0]=='T') {
			int tt=find(t);
			split(rt,tt-1,x,y);
			split(y,1,y,z);
			rt=merge(y,merge(x,z));
		} else if(op[0]=='B'){
			int tt=find(t);
			split(rt,tt-1,x,y);
			split(y,1,y,z);
			rt=merge(merge(x,z),y);
		} else if(op[0]=='I') {
			int del; scanf("%d",&del);
			int tt=find(t);
			split(rt,tt-1,x,y);
			split(y,1,y,z);
			rt=merge(x,z);
			tt+=del-1;
			split(rt,tt,x,z);
			rt=merge(x,merge(y,z));
		} else if(op[0]=='A') {
			printf("%d\n",find(t)-1);
		} else {
			split(rt,t-1,x,y);
			split(y,1,y,z);
			printf("%d\n",y);
			rt=merge(x,merge(y,z));
		}
	}
	return 0;
}
posted @ 2021-03-21 15:32  wwwsfff  阅读(44)  评论(0编辑  收藏  举报