Codeforces

分析

orz yhx

为了方便我们在最后加一个 $a_i = N, b_i = N, c_i = +\infty$ 的站台,其中 $N$ 是一个很大的数。这样子我们可以保证每辆火车恰好送走 $k$ 个人。

设 $f_{i, j}$ 表示只考虑前 $i$ 个站台、要坚持 $j$ 时刻最少需要放多少辆火车,$g_{i, j}$ 表示只考虑前 $i$ 个站台、要坚持 $j$ 时刻、且把除了 $i$ 站台以外的站台上的人全送走最少需要放多少辆火车。

需要注意的是,这两个状态都需要满足每次都恰好送走 $k$ 个,如果做不到则将值设为 $+\infty$。

考虑转移,分两种情况:

  • 前 $j$ 轮中没有送走任何一个 $i$ 站台的人,则需要满足 $a_i + jb_i \leq c_i \land f_{i - 1, j} \neq +\infty$。

    此时有 $f_{i, j} \gets f_{i - 1, j}$;下面考虑 $g_{i, j}$ 的转移。

    在前 $j$ 时刻,前 $i - 1$ 个站台共有 $L = \sum_{p = 1}^{i - 1} (a_i + jb_i)$ 人,这些人需要 $\lceil \frac{L}{k} \rceil$ 辆火车送走。

    为了保证每次都恰好送走 $k$ 个人,应有$\lceil \frac{L}{k} \rceil \cdot k \leq \sum_{p = 1}^i (a_i + jb_i)$。于是在满足这个条件的时候有转移 $g_{i, j} \gets \lceil \frac{L}{k} \rceil$。

  • 前 $j$ 轮送走了 $i$ 站台的人,不妨设最后一次是 $p$ 时刻。

    于是前 $p$ 时刻中清空了前 $i - 1$ 个站台,这部分需要 $g_{i, p}$ 辆火车。

    那么在 $p$ 时刻 $i$ 站台还剩下 $r = a_i + pb_i - kg_{i, p}$ 个人,于是在 $j$ 时刻 $i$ 站台会有 $r + (j - p)b_i$ 个人。

    所以在 $p$ 时刻我们需要派出 $need = \left\lceil \frac{\max\{r + (j - p)b_i - c_i, 0\}}{k} \right\rceil$ 辆火车送走 $i$ 站台上的若干人,使得它在 $j$ 时刻不会超出人数限制。同样的,为了保证每次恰好送走 $k$ 个人,应有 $need \cdot k \leq r$。

    如果 $need \cdot k \leq r$ 成立,那么在接下来 $j - p$ 时刻中相当于前 $i - 1$ 个站台、$a_i = 0$ 的子问题。

    $a_i = 0$ 并不难处理,我们可以对 $f, g$ 都加一维 $0 / 1$,表示初始人数是 $a_i$ 还是 $0$。那么这个子问题的答案就是 $f_{i - 1, j - p, 0}$。因此 $f_{i, j, 0/1}$ 的转移为 $f_{i, j, 0/1} \gets g_{i, p, 0/1} + need + f_{i - 1, j - p, 0}$。

    然后考虑 $g_{i, j, 0/1}$ 的转移,和上面类似,总人数为 $L = \sum_{q = 1}^{i - 1} (j - r)b_i$,需要派出 $\lceil \frac{L}{k} \rceil$ 辆火车,同样判断一下是否能每次都恰好送走 $k$ 个人即可。如果可行则转移为 $g_{i, j, 0/1} \gets g_{i, p, 0/1} + need + \lceil\frac{L}{k}\rceil$。

边界是 $f_{0, i, 0/1} = g_{0, i, 0/1} = 0$,答案为 $f_{n, t, 1}$。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
typedef long long ll;

template<typename T>
void chkmin(T &x, T y) { if (y < x) x = y; }
int read() {
    int X = 0, w = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
    while (c >= '0' && c <= '9') X = X * 10 + c - '0', c = getchar();
    return X * w;
}

const int N = 200 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;

int n, t, s;
ll a[N], b[N], c[N], sa[N], sb[N];
ll f[N][N][2], g[N][N][2];

int main() {
    n = read(), t = read(), s = read();
    for (int i = 1; i <= n; ++i) a[i] = read(), b[i] = read(), c[i] = read();
    ++n, a[n] = b[n] = 2e9, c[n] = inf;
    for (int i = 1; i <= n; ++i) sa[i] = sa[i - 1] + a[i], sb[i] = sb[i - 1] + b[i];
    memset(f, 0x3f, sizeof(f)), memset(g, 0x3f, sizeof(g));
    for (int i = 0; i <= t; ++i)
        f[0][i][0] = f[0][i][1] = g[0][i][0] = g[0][i][1] = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = 0; j <= t; ++j)
            for (int k = 0; k <= 1; ++k) {
                if (k * a[i] + j * b[i] <= c[i] && f[i - 1][j][k] != inf) {
                    chkmin(f[i][j][k], f[i - 1][j][k]);
                    ll cnt = (k * sa[i - 1] + j * sb[i - 1] + s - 1) / s;
                    if (cnt * s <= k * sa[i] + j * sb[i]) chkmin(g[i][j][k], cnt);
                }
                for (int p = 0; p < j; ++p) {
                    if (g[i][p][k] == inf) continue;
                    ll res = k * sa[i] + p * sb[i] - s * g[i][p][k];
                    ll need = (max(res + (j - p) * b[i] - c[i], 0ll) + s - 1) / s;
                    if (need * s <= res && f[i - 1][j - p][0] != inf) {
                        chkmin(f[i][j][k], g[i][p][k] + need + f[i - 1][j - p][0]);
                        ll cnt = ((j - p) * sb[i - 1] + s - 1) / s;
                        if (cnt * s <= (j - p) * sb[i] + res - need * s)
                            chkmin(g[i][j][k], g[i][p][k] + need + cnt);
                    }
                }
            }
    printf("%lld\n", f[n][t][1]);
    return 0;
}
最后修改:2021 年 03 月 29 日 10 : 13 PM