普通平衡树 离散化

        COGS  普通平衡树

【题目描述】

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

【输入格式】

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

【输出格式】

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

【样例输入】

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

【样例输出】

106465
84185
492737

【提示】

1.n的数据范围:n<=100000

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


线段树做法:

题解:对于数据范围,我们可以现将输入的数据进行离散化,对于每一个输入的数据建立线段树的节点,在每一个节点内保存区间和,代表着比他大的树有多少个,对于插入和删除一个数,我们可以利用insert+1或-1就好了,查询排名的时候,我们只需要查询1到x-1的个数+1,查询X的排名的时候,可以分别查找左右子树,对于5,6操作,我们可以在区间1到x-1或x+1到N中查找,在查找的过程中例如查找前趋,我们可以再查找的时候加一个判断,看看在这个子树中能不能找到,如果不能找到,再返回另一个子树中的值,但是这样做会导致时间效率很低,导致开始我T了5个点,所以这个时候我们可以在线段树的每个节点中多保存一个maxn(前驱)和minn(后继),这样在查找的时候就可以大大缩短时间效率。


在放代码之前我们先看一下离散化怎样去写:

        因为输入的数只有100000个,岁我们可以把这100000个数先排序,排完序以后再去重,然后我们再将每个数中的排名加入到数据中就好了,最后再加上一个数的映射。

int main()
{
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	{
		scanf("%d%d",&a[i].kind,&a[i].xx);
		if(a[i].kind!=4)
		{
			tot+=1;
			c[tot]=a[i].xx;
		}
	}
	sort(c+1,c+tot+1);
	size=unique(c+1,c+tot+1)-c-1;  //  unique()是一个去重的函数,函数返回值是去掉重复元素后最后一位在数组中的位置,所以我们在使用的时候应该讲数据先进行排序,值得一提的是,再去重的过程中并没有把重复的元素删去,而是保留在了数组的最后. 
	                               //  size 可以看做是长度 
	for(i=1;i<=n;++i)
	{
		if(a[i].kind!=4)
		{
			tot=a[i].xx;
			a[i].xx=upper_bound(c+1,c+size+1,a[i].xx)-c-1;  //将a[]中的元素换成排名 
			d[a[i].xx]=tot;  //d[] 为一个映射。 
		}
	}
}
	

 下面放上代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#define L k<<1,l,mid
#define R k<<1|1,mid+1,r
#include<algorithm>
using namespace std;
struct point 
{
	int kind,xx;
}a[100001];
struct S
{
	int sum,maxn,minn;
}tr[400000];
int n,c[100001]={0},d[100001]={0},size;
inline void updata(int k)
{
    tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
    if (tr[k<<1|1].maxn==0) tr[k].maxn=tr[k<<1].maxn;
    else tr[k].maxn=tr[k<<1|1].maxn;
    if (tr[k<<1].minn==0) tr[k].minn=tr[k<<1|1].minn;
    else tr[k].minn=tr[k<<1].minn;
}
inline void insert(int k,int l,int r,int x,int y)
{
	int mid=(l+r)>>1;
	if(l==r)
	{
		tr[k].sum+=y;
		if(tr[k].sum!=0) tr[k].maxn=tr[k].minn=l;
		else tr[k].maxn=tr[k].minn=0;
		return ;
	}
	if(x<=mid) insert(L,x,y);
	else insert(R,x,y);
	updata(k);
}
inline int NO(int k,int l,int r,int x,int y)
{
	int mid=(l+r)>>1,num=0;
	if(x<=l&&y>=r) return tr[k].sum;
	if(x<=mid) num=NO(L,x,y);
	if(y>mid) num+=NO(R,x,y);
	return num;
}
inline int work(int k,int l,int r,int x)
{
	int mid=(l+r)>>1,num,sum;
	if(l==r) return l;
	if(x<=tr[k<<1].sum) return work(L,x);
	else return work(R,x-tr[k<<1].sum);
}
struct S work1(int k,int l,int r,int x,int y)
{
	int mid=(l+r)>>1;
	struct S ans,ans1,ans2;
	ans1.maxn=ans1.minn=ans2.maxn=ans2.minn=0;
	if(x<=l&&y>=r) return tr[k];
	if(x<=mid) ans1=work1(L,x,y);
	if(y>mid) ans2=work1(R,x,y);
	if(ans1.minn) ans.minn=ans1.minn;
	else ans.minn=ans2.minn;
	if(ans2.maxn) ans.maxn=ans2.maxn;
	else ans.maxn=ans1.maxn;
	return ans;
}
int main()
{
	freopen("phs.in","r",stdin);
	freopen("phs.out","w",stdout);
	int i,j,t,x,y,tot=0;
	struct S ans;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	{
		scanf("%d%d",&a[i].kind,&a[i].xx);
		if(a[i].kind!=4)
		{
			tot+=1;
			c[tot]=a[i].xx;
		}
	}
	sort(c+1,c+tot+1);
	size=unique(c+1,c+tot+1)-c-1;
	for(i=1;i<=n;++i)
	{
		if(a[i].kind!=4)
		{
			tot=a[i].xx;
			a[i].xx=upper_bound(c+1,c+size+1,a[i].xx)-c-1;
			d[a[i].xx]=tot;
		}
	}
	for(i=1;i<=n;++i)
	{
		if(a[i].kind==1) insert(1,1,size,a[i].xx,1);
		if(a[i].kind==2) insert(1,1,size,a[i].xx,-1);
		if(a[i].kind==3) 
		{
			if(a[i].xx==1) printf("1\n");
			else printf("%d\n",NO(1,1,size,1,a[i].xx-1)+1);
		}
		if(a[i].kind==4) printf("%d\n",d[work(1,1,size,a[i].xx)]);
		if(a[i].kind==5) printf("%d\n",d[work1(1,1,size,1,a[i].xx-1).maxn]);
		if(a[i].kind==6) printf("%d\n",d[work1(1,1,size,a[i].xx+1,size).minn]);
	}
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值