BZOJ3830[Poi2014] Freight
Description
Upper Bytown和Lower Bytown的火车站被一条铁路连接着。火车从一侧到另一侧需要S分钟。然而,每两列车发车时间至少需要间隔一分钟。并且,在每一个时刻,在铁路上的所有列车的行驶方向都必须相同。
按照时间表的顺序,N辆列车将从Upper Bytown出发前往Lower Bytown,到达后又需要回到Upper Bytown。
请计算最迟回到Upper Bytown的列车回到Upper Bytown的时刻的最小值。
Input
第一行包括两个整数N,S(1<=N<=1 000 000,1<=A<=10^9),以一个空格隔开。表示列车的数量和单向行驶的时间。
第二行包括N个整数T1,T2…Tn(0<=T1<=T2..<=Tn<=10^9),以一个空格分隔,表示每辆车的最早发车时刻。
Output
打印一个整数,表示最迟回到Upper Bytown的列车回到Upper Bytown的时刻的最小值。
Sample Input
3 4
1 8 11
Sample Output
20
Solution:
试了很久想写贪心,结果无果。于是只好dp了。
我们把它转化为一个序列分割问题,因为总是一段一段的列车出发再一起回来。定义
dp[i]
表示前
i
辆车全部回来的最小时间,根据题意,我们可以列出以下的状态转移方程:(注意发车要有间隔)
首先可以发现 dp[i]−dp[i−1]>=1 ,即 dp 数组单调递增,于是 dp[j]−j+i−1 也是单调的。于是可以分段处理:
对于下面这一段,我们要使 j 最大,于是可以在
但是对于上一种情况,因为 dp[i] 的增长速度大于等于 2⋅i ( dp 每次至少加2),因此肯定是最前面最优,即转移点的转移来的 dp 值呈一个V字形,且这个最优点一直在单调前移,于是就可以 O(n) 解决了。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define M 1000005
#define INF 1LL<<62
using namespace std;
inline void Rd(int &res){
char c;res=0;
while(c=getchar(),c<'0');
do{
res=(res<<1)+(res<<3)+(c^48);
}while(c=getchar(),c>='0');
}
/*dp[i]=Max(dp[j]+i-j-1,A[i])+i-j-1+2*s*/
ll dp[M];
int n,s,A[M];
void check(ll &a,ll b){
if(a==-1||a>b)a=b;
}
int main(){
Rd(n);Rd(s);
for(int i=1;i<=n;i++){
Rd(A[i]);dp[i]=INF;
}
sort(A+1,A+n+1);
for(int i=2;i<=n;i++)
A[i]=max(A[i-1]+1,A[i]);
int j=0;
for(int i=1;i<=n;i++,j--)
while(j<i&&max(dp[j]+i-j-1,(ll)A[i])+i-j-1+2*s<dp[i])dp[i]=max(dp[j]+i-j-1,(ll)A[i])+i-j-1+2*s,j++;
cout<<dp[n]<<endl;
return 0;
}