BZOJ3830[Poi2014] Freight

30 篇文章 0 订阅
16 篇文章 0 订阅

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 辆车全部回来的最小时间,根据题意,我们可以列出以下的状态转移方程:(注意发车要有间隔)

Minimizedp[i]dp[i]=max(dp[j]+ij1,A[i])+2s+ij1

首先可以发现 dp[i]dp[i1]>=1 ,即 dp 数组单调递增,于是 dp[j]j+i1 也是单调的。于是可以分段处理:

dp[i]={dp[j]+ij1+2s+ij1,A[i]+2s+ij1,dp[j]+ij1>A[i]dp[j]+ij1<=A[i]

对于下面这一段,我们要使 j 最大,于是可以在dp数组里直接二分这个点 j 。上面一段就是求[j,i)区间的最小值,用树状数组维护。复杂度 O(nlogn)

但是对于上一种情况,因为 dp[i] 的增长速度大于等于 2i ( 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值