AtCoder Beginner Contest 265 F Manhattan Cafe
考虑 dp,\(f_{i, j, k}\) 表示考虑到第 \(i\) 维,\(\sum\limits_{x = 1}^i |p_x - r_x| = j, \sum\limits_{x = 1}^i |q_x - r_x| = k\) 的方案数。转移是容易的,枚举 \(r_i\) 即可,\(f_{i, j, k} = \sum\limits_r f_{i - 1, j - |p_i - r|, k - |q_i - r|}\)。
复杂度 \(O(nD^3)\),无法接受,考虑下标拆绝对值后前缀和优化。简单讨论一下就行。以 \(p_i \le q_i\) 的情况为例:
- \(r_i < p_i\),\(f_{i, j, k}\) 能从 \(\cdots, f_{i - 1, j - 2, k - q_i + p_i - 2}, f_{i - 1, j - 1, k - q_i + p_i - 1}\) 转移;
- \(p_i \le r_i \le q_i\),\(f_{i, j, k}\) 能从 \(f_{i - 1, j, k - q_i + p_i}, f_{i - 1, j - 1, k - q_i + p_i + 1}, \cdots, f_{i - 1, j - q_i + p_i, k}\) 转移;
- \(r_i > q_i\),\(f_{i, j, k}\) 能从 \(f_{i - 1, j - b_i + a_i - 1, k - 1}, f_{i - 1, j - b_i + a_i - 2, k - 2}, \cdots\) 转移。
也就是说我们只要维护对角线前缀和就好了。
要注意下标越界的问题。对于第 \(1, 3\) 种情况,能转移的是一段前缀或后缀,只要特判下标是否越界即可;对于第 \(2\) 种情况,因为能转移的是一段区间,如果下标为负,就要找到第一个 \(0\) 开始转移。
想清楚了写起来挺快的,不存在难写的问题。注意一些细节问题即可,比如符号写反,或者没取模等等,别像我一样有一处漏了取模然后傻傻调试 20min 就行。
时间复杂度 \(O(nD^2)\),可以通过。
code
// Problem: F - Manhattan Cafe
// Contest: AtCoder - AtCoder Beginner Contest 265
// URL: https://atcoder.jp/contests/abc265/tasks/abc265_f
// Memory Limit: 1024 MB
// Time Limit: 6000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 1010;
const int mod = 998244353;
int n, m, a[maxn], b[maxn], f[2][maxn][maxn], g[3][maxn][maxn];
void solve() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &b[i]);
}
f[0][0][0] = 1;
for (int i = 1, o = 1; i <= n; ++i, o ^= 1) {
mems(f[o], 0);
mems(g, 0);
for (int j = 0; j <= m; ++j) {
for (int k = 0; k <= m; ++k) {
g[0][j][k] = f[o ^ 1][j][k];
if (j && k) {
g[0][j][k] = (g[0][j][k] + g[0][j - 1][k - 1]) % mod;
}
}
}
for (int j = m; ~j; --j) {
for (int k = 0; k <= m; ++k) {
g[1][j][k] = f[o ^ 1][j][k];
if (j < m && k) {
g[1][j][k] = (g[1][j][k] + g[1][j + 1][k - 1]) % mod;
}
}
}
for (int j = 0; j <= m; ++j) {
for (int k = m; ~k; --k) {
g[2][j][k] = f[o ^ 1][j][k];
if (j && k < m) {
g[2][j][k] = (g[2][j][k] + g[2][j - 1][k + 1]) % mod;
}
}
}
for (int j = 0; j <= m; ++j) {
for (int k = 0; k <= m; ++k) {
if (a[i] <= b[i]) {
if (j - 1 >= 0 && k - b[i] + a[i] - 1 >= 0) {
f[o][j][k] = (f[o][j][k] + g[0][j - 1][k - b[i] + a[i] - 1]) % mod;
}
if (j + 1 <= m && k - b[i] + a[i] - 1 >= 0) {
f[o][j][k] = (f[o][j][k] + mod - g[1][j + 1][k - b[i] + a[i] - 1]) % mod;
}
int x = j - b[i] + a[i], y = k;
if (x < 0) {
y += x;
x = 0;
}
if (y >= 0) {
f[o][j][k] = (f[o][j][k] + g[1][x][y]) % mod;
}
if (j + a[i] - b[i] - 1 >= 0 && k - 1 >= 0) {
f[o][j][k] = (f[o][j][k] + g[0][j + a[i] - b[i] - 1][k - 1]) % mod;
}
} else {
if (j - a[i] + b[i] - 1 >= 0 && k - 1 >= 0) {
f[o][j][k] = (f[o][j][k] + g[0][j - a[i] + b[i] - 1][k - 1]) % mod;
}
if (j - a[i] + b[i] - 1 >= 0 && k + 1 <= m) {
f[o][j][k] = (f[o][j][k] + mod - g[2][j - a[i] + b[i] - 1][k + 1]) % mod;
}
int x = j, y = k - a[i] + b[i];
if (y < 0) {
x += y;
y = 0;
}
if (x >= 0) {
f[o][j][k] = (f[o][j][k] + g[2][x][y]) % mod;
}
if (j - 1 >= 0 && k + b[i] - a[i] - 1 >= 0) {
f[o][j][k] = (f[o][j][k] + g[0][j - 1][k + b[i] - a[i] - 1]) % mod;
}
}
}
}
}
int ans = 0;
for (int i = 0; i <= m; ++i) {
for (int j = 0; j <= m; ++j) {
ans = (ans + f[n & 1][i][j]) % mod;
}
}
printf("%d\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}