B. Bonuses on a Line
给出线段上的点坐标,一个人在原点,可以走t的路程,问最多可以经过几个点。朴素想到贪心,后面感觉贪心情况太复杂,策略不一定对。可以分成4种情况,只往左,只往右,先左再右,先右再左。显然,问题可以转化。将负数段,正数段分别存储。相当于在两个数组里选点数尽可能多的两段,使得其中一段的长度*2+另一段的长度<=t。对于后两种情况,当一个端点定下来的时候,另一端的极限就定了。对于前两种情况也就相当于一端定在0,查找另一端极限的方式是,减去已经用掉的部分。用upper_bound查找剩下的部分可以经过的最多点数令就可以了。
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<vector>
#include<functional>
using namespace std;
typedef long long LL;
inline LL read()
{
LL kk=0,f=1;
char cc=getchar();
while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
return kk*f;
}
const int maxn=1000222;
LL a[maxn],l,r,mid,t,n,ans;
vector<LL>fi,se;
void gao()
{
ans=upper_bound(fi.begin(),fi.end(),t)-fi.begin();//只向一边走
ans=max(ans,(LL)(upper_bound(se.begin(),se.end(),t)-se.begin()));
for(int i=0;i<se.size();++i)//枚举右端点
{
if(t>se[i])//先往左再往右,
{
LL kk=(t-se[i])/2;//左边两倍
LL num=upper_bound(fi.begin(),fi.end(),kk)-fi.begin();
ans=max(i+num+1,ans);
}
if(t>se[i]*2)//先右再左
{
LL kk=t-2*se[i];//右边两倍
LL num=upper_bound(fi.begin(),fi.end(),kk)-fi.begin();
ans=max(i+num+1,ans);
}
}
}
int main()
{
n=read();t=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
if(a[i]<0)fi.push_back(-a[i]);//负数段
else se.push_back(a[i]);//正数段
}
sort(fi.begin(),fi.end());
gao();
printf("%lld\n",ans);
}