洛谷 P5307
考虑一个最暴力的 DP,设 \(f_{i,j,k}\) 表示走到 \((i,j)\) 乘积为 \(k\) 的路径数,显然过不了。
考虑优化一下状态设计,设 \(f_{i,j,k}\) 表示走到 \((i,j)\) 还要至少乘上 \(k\) 才能不小于 \(n\) 的路径数,显然 \(k\) 形如 \(\left\lceil \frac{n}{i}\right \rceil\) 这种形式,至多 \(\mathcal O(\sqrt n)\) 种,预处理出来离散化后再转移即可。
注意把第一维滚动掉否则会 MLE。
时间复杂度 \(\mathcal O(rs\sqrt n)\)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 305, M = 2005, W = 1000005, mod = 1e9 + 7;
int n, m, K;
int a[N][N];
int f[2][N][M];
int val[M], tot, pos[W], b[W];
void add(int &a, int b) { if ((a += b) >= mod) a -= mod; }
int calc(int x, int y) { return x % y == 0 ? x / y : x / y + 1; }
int main() {
scanf("%d%d%d", &n, &m, &K);
for (int i = 1; i <= K; ++i) b[i] = calc(K, i);
for (int i = 1; i <= K; ++i) if (b[i] != b[i - 1]) pos[val[++tot] = b[i]] = tot;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
scanf("%d", &a[i][j]);
f[1][1][pos[calc(K, a[1][1])]] = 1;
for (int i = 1; i <= n; ++i) {
int cur = i & 1, nxt = cur ^ 1;
for (int j = 1; j <= m; ++j)
for (int k = 1; k <= tot; ++k) {
if (j < m) add(f[cur][j + 1][pos[calc(val[k], a[i][j + 1])]], f[cur][j][k]);
if (i < n) add(f[nxt][j][pos[calc(val[k], a[i + 1][j])]], f[cur][j][k]);
if (i != n || j != m || k != tot) f[cur][j][k] = 0;
}
}
printf("%d", f[n & 1][m][tot]);
return 0;
}

浙公网安备 33010602011771号