大致题意:有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:大胆放开思路,有时就会有意想不到的收获。