普通平衡树 HYSBZ - 3224 ---权值线段树写法

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
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的数据范围:n<=100000

 

2.每个数的数据范围:[-2e9,2e9]

 

 

//#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int M = 1000000+7;
struct node
{
	int c,x;
}lx[M];
int a[M],b[M],la[M];
int mp[M];//最后要输出的表 
int st[M<<2];
void update(int l,int r,int rt,int x,int d)
{
	if(l==r)
	{
		st[rt]+=d;
		return ;
	}
	int mid=(l+r)/2;
	if(x<=mid)update(l,mid,rt<<1,x,d);
	else update(mid+1,r,rt<<1|1,x,d);
	st[rt]=st[rt<<1]+st[rt<<1|1];
}
int xrank(int l,int r,int rt,int L,int R)
{
	if(L<=l&&r<=R)
	{
		return st[rt];
	}
	int mid=(l+r)/2;
	int res=0;
	if(L<=mid)res+=xrank(l,mid,rt<<1,L,R);
	if(R>mid)res+=xrank(mid+1,r,rt<<1|1,L,R);
	return res;
}
int Kith(int l, int r,int rt,int k)
{
	if(l==r)return l;//  l表示的是权值 
	int mid=(l+r)>>1;
	if(st[rt<<1]>=k)return Kith(l,mid,rt<<1,k);
	return Kith(mid+1,r,rt<<1|1,k-st[rt<<1]);
}

int Findpre(int p,int l,int r)
{
	if(l==r)return l;
	int mid=(l+r)>>1;
	if(st[p<<1|1])return Findpre(p<<1|1,mid+1,r);//右子树非空向右找 
	return Findpre(p<<1,l,mid);//否则向左找 
}
//找前驱 尽可能在小于v的右子树找 
int Pre(int p,int l,int r,int v)
{
	if(r<v)//maxr<v即在p的子树中 p区间内最右非空子树即答案 
	{
		if(st[p])return Findpre(p,l,r);
		return 0;
	}
	int mid=(l+r)>>1,Re;
	//如果v在右子树可能有前驱(至少mid+1比v小)就先查右子树,l=mid+1 
	if(mid+1<v&&st[p<<1|1]&&(Re=Pre(p<<1|1,mid+1,r,v)))return Re;
	//否则查左子树,r=mid,使r不断变小直至满足题意小于v 
	return Pre(p<<1,l,mid,v);
} 
 
int Findnext(int p,int l,int r)
{
	if(l==r)return l;
	int mid=(l+r)>>1;
	if(st[p<<1])return Findnext(p<<1,l,mid);
	return Findnext(p<<1|1,mid+1,r);
} 
 
//找后继 尽可能在大于v的左子树找 
int Next(int p,int l,int r,int v)
{
	if(v<l)//已找到大于v的最小完整区间 
	{
		if(st[p])return Findnext(p,l,r); 
		return 0;
	}
	int mid=(l+r)>>1,Re;
	//如果左子树里有比v大的(至少mid比v大)就查左子树 否则查右子树 
	if(v<mid&&st[p<<1]&&(Re=Next(p<<1,l,mid,v)))return Re;
	return Next(p<<1|1,mid+1,r,v);
}
int main()
{
	int n,N=100000;
	cin>>n;
	int c,x;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&c,&x);
		lx[i].x=x;
		lx[i].c=c;
		b[i]=a[i]=x;
	}
	sort(b+1,b+1+n);
	for(int i=1;i<=n;i++)
	{
		la[i]=lower_bound(b+1,b+1+n,a[i])-b;
		mp[la[i]]=a[i];
	//	printf("%d   %d\n",la[i],mp[la[i]]);
	}
	for(int i=1;i<=n;i++)
	{
		c=lx[i].c;
		x=la[i];
		if(c==1)update(1,N,1,x,1);//插入x
		if(c==2)update(1,N,1,x,-1);//删除x
		if(c==3)printf("%d\n",xrank(1,N,1,1,x-1)+1);//查询x的排名-从小到大
		if(c==4)x=lx[i].x,printf("%d\n",mp[Kith(1,N,1,x)]);//查询第k小
		if(c==5)printf("%d\n",mp[Pre(1,1,N,x)]);//查询x的前驱
		if(c==6)printf("%d\n",mp[Next(1,1,N,x)]);//查询x的后继
	}
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值