The Preliminary Contest for ICPC Asia Xuzhou 2019,E(思维)

本文深入解析了一种新颖的区间匹配算法,该算法用于解决特定的能力值匹配问题。通过对能力值数组进行排序和巧妙的遍历策略,算法能够在限定时间内高效找出每个人在加入buff后的最右匹配者,并计算匹配范围内的人员数量。

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

大致题意:有n个人,有n个数a[n],a[i]表示第i个人的能力值,给定一个m,m为buff值,现在问你对于第i人(1<=i<=n)人的能力值加上buff后,找到最右边的能力值大于他的人的位置j,然后输出i~j之间有多少个人,如果找不到则输出-1。最后要输出n个数这样的数,对应每一个人。

最近做的区间题比较多,所以想法也逐渐大胆,最后也竟AC了。
思路:将a[n]设置为结构体,a[n].val表示能力值,a[n].id表示编号,另外设置a0[n]保留原始的能力值数组。先将a[n]按能力值从小到大排序,能力值相同的编号小的在前。下面就进入主算法内容了。
在将主算法之前,我们需要明确两个事实:
1.假设第i个人成功匹配,即他找到了其对应的最右边的人j,也就是a0[i]+m<=a0[j],那么在[i+1,j-1]之间所有能力值小于等于a0[i]的,j都是他们所对应的那个人,即i+1<=k<=j-1,若a0[k]<=a0[i],则a0[k]+m<=a0[j];
2.假设第i个人失配,即他并没有找到对应的符合要求的人,那么在[i+1,n]之间所有能力值大于等于a0[i]的,也都找不到符合要求的人了。

于是,我们将a[n]排完序后,从前往后遍历a[n],同时设置temp和mi,temp表示找到的符合要求的人的编号,mi表示失配的人当中的最小编号。因为我们已经将a[n]的能力值从小到大排序了,这会在后面会有起到很大的帮助。
假设我们现在以及匹配到了第i个人,那么会有如下情况:
1.a[i].id<temp,说明temp或许可以与i成功匹配,进行再一步的判断:
①a0[a[i].id]+m<=a0[temp],temp与i成功匹配;
②a0[a[i].id]+m>a0[temp],失配,更新temp,因为排序的原因,所以我们只需从[i+1.temp-1]当中去找下一个符合要求的人,找到的就更新temp的值,找不到,更新mi,mi=min(mi,a[i].id)。
2.a[i].id>=temp,此时temp不可能与i匹配,进行下一步的判断:
①a[i].id>=mi,因为排序的原因,所以i是肯定找不到的了,直接判定失配;
②a[i].id<mi,执行1.②的操作。

时间限制1000MS,AC300MS,还是挺可观的。
代码如下:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
struct number
{
	ll val;
	int id;
}a[maxn];
ll a0[maxn];	//原始能力值数组
int ans[maxn];	//ans[i]对应编号为i的人的答案

bool cmp(number x,number y)	//排序
{
	if(x.val!=y.val)
		return x.val<y.val;
	return x.id<y.id;
}

int main()
{
	int n;	ll m;
	scanf("%d%lld",&n,&m);
	for(int i=1;i<=n;++i)
	{
		scanf("%lld",&a[i].val);
		a[i].id=i;
		a0[i]=a[i].val;
	}
	sort(a+1,a+1+n,cmp);
	int temp=n+1,mi=n+1;	a0[n+1]=0;	//temp和mi一开始初始化为n+1,亲测好使
	for(int i=1;i<=n;++i)
	{
		int id=a[i].id;
		if(id<temp)	//有可能匹配
		{
			if(a0[id]+m<=a0[temp])	//成功匹配
				ans[id]=temp-1-id;
			else	//失配
			{
				bool flag=0;	//flag标志i能否成功匹配
				for(int j=temp-1;j>id;--j)
				{
					if(a0[id]+m<=a0[j])	//成功
					{
						flag=1;
						temp=j;
						break;	//这时就可以结束了
					}
				}
				if(!flag)		//失败了
				{	
					mi=min(mi,id);	//更新mi
					ans[id]=-1;
					temp=id;	//这里是一个剪枝,找不到就可以直接把temp更新为id,同样也是因为排了序才能这么做
								//少了这步会超时,自行理解好
				}
				else 	//成功匹配
				{
					ans[id]=temp-1-id;
				}
			}
		}
		else if(id>=mi)	//在[mi,n]之间,失配
			ans[id]=-1;
		else	//为i寻找下一个合适的人
		{
			bool flag=0;
			for(int j=temp-1;j>id;--j)
			{
				if(a0[id]+m<=a0[j])
				{
					flag=1;
					temp=j;
					break;
				}
			}
			if(!flag)	
			{
				mi=min(mi,id);
				ans[id]=-1;
				temp=id;
			}
			else 
			{
				ans[id]=temp-1-id;
			}
		}
	}
	for(int i=1;i<=n;++i)
	{
		printf("%d",ans[i]);
		if(i!=n)	putchar(' ');
		else putchar('\n'); 
	}
	return 0;
}

PS:大胆放开思路,有时就会有意想不到的收获。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值