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

本文介绍了一种使用平衡树解决数据维护问题的方法,包括插入、删除、查询排名、求前驱和后继等操作。通过具体示例和代码实现,展示了平衡树在处理大量数据时的高效性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

插入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;
} 

### Treap树的基本原理 Treap(Tree + Heap)是一种结合了二叉搜索树(BST)堆(Heap)特性的数据结构。每个节点包含两个属性:一个用于满足二叉搜索树性质的关键(key),以及一个用于满足堆性质的随机优先级(priority)。Treap通过维护这两个性质来实现高效的插入、删除查找操作。 - **二叉搜索树性质**:对于任意节点,其左子树中的所有节点的关键都小于该节点的关键,而右子树中的所有节点的关键都大于该节点的关键。 - **堆性质**:每个节点的优先级都大于其子节点的优先级,这样可以确保树的高度在期望上是平衡的。 Treap的随机优先级设计使得树的结构在期望上接近于完全平衡的状态,从而保证了平均时间复杂度为 $O(\log n)$ 的操作性能 [^3]。 ### Treap的实现方式 Treap的实现主要包括插入、删除、查找等操作,其核心机制依赖于旋转操作分裂/合并操作。 1. **插入操作**:插入一个新节点时,首先按照二叉搜索树的方式找到合适的位置。插入后,如果新节点的优先级大于其父节点的优先级,则通过旋转操作调整树的结构以维护堆性质。 2. **删除操作**:删除节点时,需要通过旋转操作将该节点“下沉”到叶子位置,然后直接删除。或者,可以将树分裂为两部分,删除目标节点后重新合并。 3. **查找操作**:查找操作与普通二叉搜索树相同,按照关键进行比较并递归地在左子树或右子树中查找。 4. **分裂与合并操作**:分裂操作将树分成两部分,通常基于某个关键或路径;合并操作则将两棵树合并为一棵,前提是它们的关键范围不重叠 [^5]。 以下是Treap的基本插入操作的伪代码示例: ```python class TreapNode: def __init__(self, key, priority): self.key = key self.priority = priority self.left = None self.right = None def insert(root, key, priority): if root is None: return TreapNode(key, priority) elif key < root.key: root.left = insert(root.left, key, priority) if root.left.priority > root.priority: root = rotate_right(root) else: root.right = insert(root.right, key, priority) if root.right.priority > root.priority: root = rotate_left(root) return root def rotate_right(y): x = y.left T2 = x.right x.right = y y.left = T2 return x def rotate_left(x): y = x.right T2 = y.left y.left = x x.right = T2 return y ``` ### Treap的应用场景 Treap在实际应用中适用于以下场景: 1. **简化代码复杂度**:Treap的实现相对简单,尤其在不需要严格平衡的场景中,其插入删除操作的常数因子较低,适合需要快速实现的项目 [^1]。 2. **动态数据集合**:当数据集合需要频繁插入删除时,Treap的随机优先级设计可以有效地保持树的平衡,从而保证高效的查找性能。 3. **概率性平衡需求**:Treap的平衡性依赖于随机优先级,适用于对概率性平衡有一定容忍度的应用场景 [^2]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值