BZOJ3224 普通平衡树

Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

Input

  第一行为n,表示操作的个数
  下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output

  对于操作3,4,5,6每行输出一个数,表示对应答案

Sample Input

10

1 106465

4 1

1 317721

1 460929

1 644985

1 84185

1 89851

6 81968

1 492737

5 493598

Sample Output

106465

84185

492737

Hint

【数据规模与约定】1≤n≤10^5,−10^7≤x≤10^7


Treap的板子题啊,treap的常数比splay小,跑得比splay块,treap很好写,splay很难调,所以我选择写splay= =

首先先让我们明确treap和splay的作用:

旋转treap与splay左右相同,但LCT会用到splay,所以splay比旋转treap范围更广。

但是非旋转treap能够实现可持久化,这是splay做不到的。因此建议大家酌情考虑。

然而非旋转treap看得我一脸蒙蔽。。。像最开始看splay一样= =估计还是需要时间的沉淀吧~

这里再提一下splay的删除操作,如下图:

我们找到x的前驱,然后断开左右儿子,这时,再将前驱伸展到根(显然只有左树在伸展),然后我们把x的右儿子接到前驱上(前驱没有右儿子),就完成了删除操作。

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,R) for(register int i=(L);i<=(R);++i)
#define Red(j,R,L) for(register int j=(R);j>=(L);--j)
const int N = 1e5+10;
struct Splay{
	int rt,cur,p[N],ch[N][2];
	int k[N],siz[N],cnt[N];
	#define Ls(v) ch[v][0]
	#define rs(v) ch[v][1]
	#define sum(v) siz[v]=siz[Ls(v)]+siz[rs(v)]+cnt[v]
	inline void rot(int x){
		int f=p[x],gf=p[f],type=ch[f][1]==x,son=ch[x][!type];
		ch[p[son]=f][type]=son,sum(f);
		ch[p[f]=x][!type]=f,sum(x);
		ch[p[x]=gf][ch[gf][1]==f]=x;
		//p[ch[gf][ch[gf][1]==f]=p[ch[x][!type]=p[ch[f][type]=son]=f]=x]=gf;//压行选手了解一下? 
	}
	inline void splay(int x){//关于splay单双旋。。。请上网查询或看我的另一篇blog 
		while(p[x]){
			if(p[p[x]]&&((ch[p[p[x]]][1]==p[x])==(ch[p[x]][1]==x)))rot(p[x]);//x和父亲以及父亲的父亲在一条链上 
			rot(x);
		}
		rt=x;
	}
	inline void insert(int &x,int fa,int v){//注意一下重复节点的处理方法 
		if(k[x]==v)return ++siz[x],++cnt[x],splay(x),void();
		if(!x)return siz[x=++cur]=cnt[x]=1,p[x]=fa,k[x]=v,splay(x),void();
		insert(ch[x][k[x]<v],x,v);
	}
	inline int FindPre(int x){
		x=Ls(x);
		while(rs(x))x=rs(x);
		return x;
	}
	inline int FindSub(int x){
		x=rs(x);
		while(Ls(x))x=Ls(x);
		return x;
	}
	inline void Delete(int x,int v){
		if(k[x]==v){
			if(cnt[x]>1)return --siz[x],--cnt[x],splay(x),void();
			splay(x);
			if(Ls(x)){//有左儿子 
				int tmp=rs(x),New=FindPre(x);
				splay(New);
				rs(p[tmp]=New)=tmp;
				sum(New);
			}else p[rs(x)]=0,rt=rs(x);//没有左儿子注意特判
			return ;
		}
		Delete(ch[x][k[x]<v],v);
	}
	inline void NumberRank(int v){
		int x=rt,rank=0;
		while(x){
			if(k[x]==v)break;
			if(k[x]<v)rank+=siz[Ls(x)]+cnt[x],x=rs(x);
			else x=Ls(x);
		}
		cout<<rank+siz[Ls(x)]+1<<"\n";
		splay(x);
	}
	inline void KthNumber(int kth){
		int x=rt;
		while(x){
			if(siz[Ls(x)]+1<=kth&&kth<=siz[Ls(x)]+cnt[x])break;
			if(kth>siz[Ls(x)]+cnt[x])kth-=siz[Ls(x)]+cnt[x],x=rs(x);
			else x=Ls(x);
		}
		cout<<k[x]<<"\n";
		splay(x);
	}
	inline void PreSub(int x,bool op){//这是极其暴力的,我们要查询一个数的前驱/后继,就先加进splay再删除= = 
		insert(rt,0,x);
		if(op==0)cout<<k[FindPre(rt)]<<"\n";
		if(op==1)cout<<k[FindSub(rt)]<<"\n";
		Delete(rt,x);
	}
}sp;
int main(){
	int n;scanf("%d",&n);
	while(n--){
		int op,x;scanf("%d%d",&op,&x);
		if(op==1)sp.insert(sp.rt,0,x);
		if(op==2)sp.Delete(sp.rt,x);
		if(op==3)sp.NumberRank(x);
		if(op==4)sp.KthNumber(x);
		if(op==5)sp.PreSub(x,0);
		if(op==6)sp.PreSub(x,1);
	}
	return 0;
}

另外treap虽然写过,但是没什么大用,不过还是给出来,顺便提一下treap:

首先它是一个BST,然后它有一个“期望值”。

除了满足BST的性质,我们还要求它的每个节点的“期望值”满足小(大)根堆的性质,即treap是BST+堆,由于“期望值是随机的”,因此能保证log2n的时间复杂度,十分好写= =

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
#define Red(i,r,L) for(register int i=(r);i>=(L);--i)
const int N = 1e5+10;
struct Treap{
	int rt,cur,k[N],siz[N],cnt[N],rank[N],c[N][2];
	#define Ls c[x][0]
	#define rs c[x][1]
	#define sum(x) siz[x]=siz[Ls]+siz[rs]+cnt[x]
	inline void rot(int &x,bool type){
		int y=c[x][type];
		c[x][type]=c[y][!type],c[y][!type]=x;
		sum(x),sum(x=y);
	}
	inline void insert(int &x,int v){
		if(!x){
			rank[x=++cur]=rand()%N;
			siz[x]=cnt[x]=1;
			k[x]=v;
			return ;
		}
		if(k[x]==v)return ++siz[x],++cnt[x],void();
		bool type=k[x]<v;
		insert(c[x][type],v);
		sum(x);
		if(rank[c[x][type]]<rank[x])rot(x,type);
	}
	inline bool Delete(int &x,int v){
		if(!x)return 0;
		if(k[x]==v){
			if(cnt[x]>1)return --siz[x],--cnt[x],1;
			if(!Ls||!rs)return x=Ls?Ls:rs,1;
			rot(x,rank[Ls]<rank[rs]?0:1);
			if(!Delete(x,v))return 0;
			return sum(x),1;
		}
		bool type=k[x]<v;
		if(!Delete(c[x][type],v))return 0;
		return sum(x),1;
	}
	inline void FindNumber_Kth(int v){
		int x=rt,rank=0;
		while(k[x]^v){
			if(k[x]<v)rank+=siz[Ls]+cnt[x],x=rs;
			else x=Ls;
		}
		cout<<rank+siz[Ls]+1<<"\n";
		
	}
	inline void FindKth_Number(int kth){
		int x=rt;
		while(x){
			if(siz[Ls]+1<=kth&&kth<=siz[Ls]+cnt[x])break;
			if(siz[Ls]<kth)kth-=siz[Ls]+cnt[x],x=rs;
			else x=Ls;
		}
		cout<<k[x]<<"\n";
	}
	inline void FindPre(int v){
		int x=rt,Pre;
		while(x){
			if(k[x]<v)Pre=k[x],x=rs;
			else x=Ls;
		}
		cout<<Pre<<"\n";
	}
	inline void FindNxt(int v){
		int x=rt,Nxt;
		while(x){
			if(k[x]>v)Nxt=k[x],x=Ls;
			else x=rs;
		}
		cout<<Nxt<<"\n";
	}
}tp;
int main(){
	srand(time(0));
	int n;scanf("%d",&n);
	while(n--){
		int op,x;scanf("%d%d",&op,&x);
		if(op==1)tp.insert(tp.rt,x);
		if(op==2)tp.Delete(tp.rt,x);
		if(op==3)tp.FindNumber_Kth(x);
		if(op==4)tp.FindKth_Number(x);
		if(op==5)tp.FindPre(x);
		if(op==6)tp.FindNxt(x);
	}
	return 0;
}

 

 

猜你喜欢

转载自blog.csdn.net/dancingz/article/details/81069400
今日推荐