codeforces gym100801 Problem J. Journey to the “The World’s Start”

传送门:https://codeforces.com/gym/100801

题意:

小明坐地铁,现在有n-1种类型的地铁卡卖,现在小明需要买一种地铁票,使得他可以在t的时间内到达终点站,地铁票的属性为r,表明这个地铁票一次最多只能做r站,可以坐到当前位置的pos-r到pos+r范围的任意一站,到站后他需要花费di的时间重新上地铁。

现在问你最小的花费是多少。

题解:

已知我们可以在pos-r到pos+r内的范围的任意一站的位置下车,那么如果我范围为r的票可以在规定时间内到达终点站的话,我的r+1的票也可以在规定的时间到达终点站,r+2的票也可以在规定的时间到达终点站,以此类推的话,我们就需要找到一个最小的r,从最小的r枚举到n就可以得到最小花费了。

于是我们需要得到最小的r。

二分答案

我们应该怎么check呢,我们定义dp状态为到达第i个站需要花费的最小时间是多少

\[dp[i] = min(dp[i - j]) + cost[i],(1 <= i <= n, 1 <= j <= min(i - 1, range) ) \]

这个min可以用单调队列优化掉,于是check 的复杂度就降到了O(n)

最终复杂度为O(nlogn)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 5;
int p[maxn], n, q[maxn];
LL d[maxn], t;
LL dp[maxn];
bool check(LL L) {
    int head, tail;
    head = tail = 1;
    q[head] = 1; dp[1] = 0;
    for(int i = 2; i <= n; i++) {
        while(tail < head && i - q[tail] > L) tail++;
        dp[i] = dp[q[tail]] + d[i];
        q[++head] = i;
        while(tail < head && dp[q[head]] <= dp[q[head - 1]]) q[head - 1] = q[head], head--;
    }
    return dp[n] <= t;
}
int main() {
    // freopen("journey.in", "r", stdin);
    // freopen("journey.out", "w+", stdout);

    scanf("%d%lld", &n, &t);
    t -= n - 1;
    for(int i = 1; i <= n - 1; ++i) {
        scanf("%d", &p[i]);
    }
    for(int i = 2; i <= n - 1; ++i) {
        scanf("%lld", &d[i]);
    }
    int L = 1;
    int l = 1, r = n;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(check(mid)) {
            r = mid - 1;
            L = mid;
        } else {
            l = mid + 1;
        }
    }
    int  ans = p[L];
    for(int i = L + 1; i < n; ++i) {
        ans = min(p[i], ans);
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2019-05-19 21:53  buerdepepeqi  阅读(161)  评论(0编辑  收藏  举报