分析
为了方便我们在最后加一个 $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;
}