题目大意:有两个城镇,从A到B需要的时间为S,然后有N辆列车需要从A到B再回来,任何时刻铁路上只能有向着同一个方向前进的列车,然后每辆列车有一个从A出发的最早出站时间,问所有列车全部回到A站的最早时间
首先经过分析可以得出,这些列车是分批行进的,就是一批列车从A到B再回来,然后下一批列车再走...一定不会出现某辆列车在某一批到了B而留在那里没有跟着下一波回A的浪潮回来而留守B地等待下一次的情况,因为这样和跟在这一批的末尾回来是等价的
所以我们就可以开始DP了,Fi表示前i辆车分了几批之后,最后一辆车回来的最早时间.Ti表示这辆车最早出发的时刻
然后可以得到DP式
Fi=min ( max( Fj+i-j-1 , T(i) )+2*S+i-j-1) )
整理一下可以得到
Fi=min ( max( Fj-j-1 , T(i)-i )+2*S-j-1) )
因为Fi-i是单调不降的,所以max( Fj-j-1 , T(i)-i )是一个一开始等于Ti-i,后来单调递增的函数
我们可以二分这个位置,前面的用Ti-i+2*S-j-1来更新答案,那Fi取最小值时j肯定在二分的临界点那里
后面的会用Fj-2*j+2*S-2来更新答案,我们可以倒着用树状数组存起来,然后查询即可
总时间复杂度O(NlogN)
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1000010
using namespace std;
long long a[N],f[N];
long long c[N];
long long n,s;
void change(long long x,long long v)
{
for(;x<=n;x+=x&-x)
c[x]=min(c[x],v);
}
long long check(long long x)
{
long long ans=707185547707185547LL;
for(;x;x-=x&-x)
ans=min(ans,c[x]);
return ans;
}
int main()
{
scanf("%lld%lld",&n,&s);
long long i,j;
scanf("%d",&a[1]);
for(i=2;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i]=max(a[i-1]+1,a[i]);
}
long long L,R,mid;
memset(c,0x3f,sizeof(c));
for(i=1;i<=n;i++)
{
L=1;R=i;
while(L<R)
{
mid=(L+R)>>1;
if(f[mid]-mid-1<=a[i]-i) L=mid+1;
else R=mid;
}
f[i]=a[i]-(L-1)+2*s+i-1;
if(L!=i) f[i]=min(f[i],check(n+1-L)+2*s+2*i);
change(n+1-i,f[i]-2*i-2);
}
printf("%lld",f[n]);
}