# 2445 普通平衡树 【非旋treap】

描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

插入x数

删除x数(若有多个相同的数,因只删除一个)

查询x数的排名(若有多个相同的数,因输出最小的排名)

查询排名为x的数

求x的前驱(前驱定义为小于x,且最大的数)

求x的后继(后继定义为大于x,且最小的数)

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

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

样例输入 [复制]
8
1 10
1 20
1 30
3 20
4 2
2 10
5 25
6 -1
样例输出 [复制]
2
20
20
20
数据规模:

n<=100000 所有数字均在-10^7 到 10 ^7内

思路:

平衡树模板,可以用非旋treap写

对于相同的数,处理的方式有很多种。这里多维护了两个数组
sam :当前权值下总数
alsam :当前子树总数(记重)
siz:当前子树总数(不记重)

3,4,询问单独写函数

PS:
其实也可以直接把相同的点加在树中,只有找到当前权值的点的时候,这同一权值的一部分才会被拆成两部分。所以 相等的时候 也向左子树走即可。

(这个代码写的有点丑。。)

其他的就是常规操作了

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define fi first
#define se second
#define in red()
inline int red()
{
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data*w;
}
const int maxn=3e5+10;
typedef pair<int,int> T;
int val[maxn],rd[maxn],son[maxn][2],sam[maxn],siz[maxn],cnt=0,root=0,alsam[maxn];
inline int push(int v){val[++cnt]=v;rd[cnt]=rand();siz[cnt]=sam[cnt]=alsam[cnt]=1;return cnt;}
inline void pushup(int k){siz[k]=siz[son[k][0]]+siz[son[k][1]]+1;alsam[k]=alsam[son[k][0]]+alsam[son[k][1]]+sam[k];}
inline int merge(int a,int b){
	if(!a||!b)return a+b;
//	if(val[a]>val[b])swap(a,b);
	if(rd[b]<rd[a]){
		 son[a][1]=merge(son[a][1],b);pushup(a);return a;
	}
	son[b][0]=merge(a,son[b][0]);pushup(b);return b;
}
inline int rank(int p,int v){
	if(!p)return 0;
	if(val[p]<=v){
		return rank(son[p][1],v)+siz[son[p][0]]+1;
	}
	return rank(son[p][0],v);
}
bool ff=1;
inline int find(int p,int v){
//	pf("p:%d val[p]: %d v:%d\n",p,val[p],v);
	if(!p)return 0;
	if(val[p]==v){
		//pf("succeed , v:%d\n",v);
		ff=0;return alsam[son[p][0]]+1;
	}
	if(val[p]<v)return find(son[p][1],v)+alsam[son[p][0]]+sam[p];
	return find(son[p][0],v);
}
inline int find2(int p,int k){
	if(!p)return 0;
	if(k<=alsam[son[p][0]]){
		return find2(son[p][0],k);
	}
	k-=(alsam[son[p][0]]+sam[p]);
	if(k<=0)return val[p];
	return find2(son[p][1],k);
}
inline T split(int p,int k){
	if(!p)return make_pair(0,0);
	T ans,tmp;
	if(k<=siz[son[p][0]]){
		 tmp=split(son[p][0],k);
		 son[p][0]=tmp.se;pushup(p);
		 ans.fi=tmp.fi;ans.se=p;
		 return ans;
	}
	tmp=split(son[p][1],k-siz[son[p][0]]-1);
	son[p][1]=tmp.fi;pushup(p);
	ans.fi=p;ans.se=tmp.se;
	return ans;
}
inline void op1(int v,bool f){
	int k=rank(root,v);
	T t=split(root,k);T l=split(t.fi,k-1);
	if(f==0){
		if(val[l.se]==v){
			//pf("f=0,val=v,val: %d\n",val[l.se]);
			++sam[l.se];++alsam[l.se];	
			root=merge(merge(l.fi,l.se),t.se);
		}
		else{
			//pf("f=0,fail;\n");
			int k1=push(v);
			root=merge(merge(merge(l.fi,l.se),k1),t.se);
		}
	}
	else{
		if(val[l.se]==v){
			
			--sam[l.se];--alsam[l.se];
			if(sam[l.se]<1){
				root=merge(l.fi,t.se);
			}	
			else{
				root=merge(merge(l.fi,l.se),t.se);
			}
		}
	}
}
inline int ask(int v,bool f){
	int k=rank(root,v);
	T t=split(root,k);T l=split(t.fi,k-1);
	int ret;
	if(f==0){
		if(val[l.se]<v)ret=val[l.se];
		else{
			T ll=split(l.fi,k-2);
			ret= val[ll.se];
			l.fi=merge(ll.fi,ll.se);
		} 
	}
	else{
		T mid=split(t.se,1);
		ret=val[mid.fi];
		t.se=merge(mid.fi,mid.se);
	}
	root=merge(merge(l.fi,l.se),t.se);
	return ret;
}
 void print(int p){
	if(son[p][0])print(son[p][0]);
	pf("%d ",val[p]);
	if(son[p][1])print(son[p][1]);
}
signed main(){
	//freopen("data.in","r",stdin);
	int n;//sf("%d",&n);
	n=in;
	while(n--){
		int f,v;f=in;v=in;
		if(f==1)op1(v,0);
		if(f==2)op1(v,1);
		if(f==3){ff=1;int ans=find(root,v);pf("%d\n",ans+ff);}
		if(f==4){pf("%d\n",find2(root,v));}
		if(f==5)pf("%d\n",ask(v,0));
		if(f==6)pf("%d\n",ask(v,1));
	}
	
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值