题意: 给出滑翔机的高度h 以及有n段上升气流区间,滑翔机在非上升气流区间会以每前进一个单位 高度就下降一个单位的 速度坠落 ,而在有上升气流的区间,滑翔机将持续前进,并不会下落,问在一维坐标上的哪个位置释放滑翔机能跑最远的距离。
题解: 首先可以明确的是,我们应该尽量走最多的上升气流区域,使得尽量延缓下降的趋势,才能在有限的高度下走最远的距离。那么出发点一定是一个上升气流区间的开始。也就是说枚举每个区间的开始位置即可。然后计算每个开始位置的飞行距离,一直取最大值即可。题目给出的上升气流区间是有序的,那么就可以在输入的过程中处理好所有非上升气流区间和上升气流区间,以便枚举计算时查询。
分别用a数组和b数组记录上升气流区间信息和非上升气流区间信息,可以知道,两种区间一定是相邻的,也就说一定是一个上升气流区间之后必跟一个非上升气流区间,是一对一对存在的。那么影响高度的只能是非上升气流区间,因此决定我们跑多远的和上升气流区间长度无关,而是非上升气流区间决定了能跑多远。在一维坐标中,任意取一段距离,这段距离内非上升气流区间的长度即决定了能飞多远。
我们要找的其实是,以一个上升气流区间的开始位置为起点,落地位置为终点的 一段 距离。因为上升气流区间没有影响因此剔除掉,剩下只用计算从开始位置,加连续几个非上升气流区间的长度,求和结果小于等于高度即符合条件,最后再加上经过几个上升气流区间,加上长度即可。不断最大化更新这个求和值,即最终结果。
这个操作可以用前缀和实现,任意一段距离即任意两点的前缀和之差。而找到第一个不超过高度的前缀和用二分即可以log(n)的复杂度计算。最终复杂度n*log(n)。
#include<bits/stdc++.h>
#define LL long long
#define M(a,b) memset(a,b,sizeof a)
#define pb(x) push_back(x)
using namespace std;
const int maxn=2e5+7;
struct node
{
LL l,r,dis;
}a[maxn],b[maxn];
LL n,h;
LL sum[maxn],fly[maxn];
int main()
{
scanf("%lld%lld",&n,&h);
sum[0]=sum[1]=0;
for(int i=0;i<n;i++)
{
scanf("%lld%lld",&a[i].l,&a[i].r);
a[i].dis=a[i].r-a[i].l;
if(i!=0)
{
b[i].l=a[i-1].r;
b[i].r=a[i].l;
b[i].dis=b[i].r-b[i].l;
sum[i]=sum[i-1]+b[i].dis;
}
fly[i]=i==0?a[i].dis:fly[i-1]+a[i].dis;
}
LL ans=0;
for(int i=0;i<n;i++)
{
int l=0,r=n-1;
while(l<r)
{
int mid=(l+r)>>1;
if(sum[mid]-sum[i]>=h) r=mid-1;
else l=mid+1;
}
if(sum[l]-sum[i]>=h)l--;
LL dist=fly[l]-fly[i]+a[i].dis+sum[l]-sum[i];
dist+=h-(sum[l]-sum[i]);
ans=max(ans,dist);
}
printf("%lld
",ans);
}