2025.04.17 CW 模拟赛 A. 稻田灌溉
A. 稻田灌溉
题目描述
小明种了 \(n\) 颗水稻,它们排成一排。
由于这 \(n\) 颗水稻的品种不一样,所以它们需要灌溉的水量也不一样,具体地:第 \(i\) 颗水稻需要被灌溉 \([a_i, b_i]\) 单位之间的水,多了会淹死,少了会旱死。
小明每次可以给一个区间 \([L_i, R_i]\) 浇水,这会使这个区间每颗水稻的水量 +1。
小明决定一共浇水 \(m\) 次。
问:小明有多少种方法浇水,可以使得每一颗水稻都能得到需要的水。
思路
看到 \(n \le 100\) 的数据, 第一眼认为是 \(\mathcal{O}(n^3)\) 的区间 \(\rm{DP}\), 但是对于两段相邻的区间不能进行合并, 所以放弃了.
转化一下问题. 我们将每一个区间看做一对括号, 那么每个位置被包含的次数需要在 \([a_i, b_i]\) 内, 求方案数.
接着考虑这样一个 \(\rm{DP}\): 我们令 \(f_{i, j, k}\) 表示当前枚举到第 \(i\) 个位置, 已经使用了 \(j\) 对括号, 当前第 \(i\) 个位置被 \(k\) 对括号包含的方案数. 显然, \(f_{i, j, k}\) 中合法的 \(k \in [a_i, b_i]\).
至于从 \(i - 1\) 进行的转移, 我们枚举 \(t, w\) 分别表示有 \(t\) 对括号从 \(i\) 开头, \(w\) 表示 \(i - 1\) 个位置被 \(w\) 对括号包含, 那么有
我们定义「延伸数量」为从 \(i - 1\) 继承到 \(i\) 的括号数量, 亦即不在 \(i - 1\) 结尾的括号数量. 在转移式子中体现为 \(k - t\), 同时因为是在 \(w\) 个里面选择 \(k - t\) 个进行「延伸」, 所以需要乘上系数 \(\binom{w}{k - t}\). 在外侧的 \(\binom{m - j - t}{t}\) 指的是在剩下的 \(m - j - t\) 个里面选择 \(t\) 个从 \(i\) 开头的方案数.
直接转移复杂度是 \(\mathcal{O}(nm^4)\) 的. 不难发现从 \(i - 1\) 转移到 \(i\) 对于每一个 \(t\) 转移的实质上是一段后缀, 预处理即可, 时间复杂度 \(\mathcal{O}(nm^3)\).
#include <iostream>
using namespace std;
constexpr int MOD = 998244353;
int n, m, C[301][301], a[101], b[101], f[101][301][301], sum[101][301][301];
__int128 val;
int add(int x, int y) {
(x += y) >= MOD and (x -= MOD);
return x;
}
void addon(int& x, int y) {
(x += y) >= MOD and (x -= MOD);
}
void init() {
cin >> n >> m;
for (int i = 0; i <= m; ++i) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; ++j) {
C[i][j] = add(C[i - 1][j], C[i - 1][j - 1]);
}
}
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 1; i <= n; ++i) {
cin >> b[i];
}
}
void calculate() {
f[0][0][0] = sum[0][0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= m; ++j) {
for (int k = a[i]; k <= b[i]; ++k) {
val = 0;
for (int t = min(j, k); ~t; --t) {
val += 1ll * sum[i - 1][j - t][k - t] * C[j][t];
}
addon(f[i][j][k], val % MOD);
}
for (int k = 0; k <= b[i + 1]; ++k) {
val = 0;
for (int t = b[i]; t >= k; --t) {
val += 1ll * f[i][j][t] * C[t][k];
}
sum[i][j][k] = val % MOD;
}
}
}
int ans = 0;
for (int i = a[n]; i <= b[n]; ++i) {
addon(ans, f[n][m][i]);
}
cout << ans << '\n';
}
void solve() {
cin.tie(nullptr)->sync_with_stdio(false);
init();
calculate();
}
int main() {
solve();
return 0;
}

浙公网安备 33010602011771号