T1
一个网格,每个点有权值,求有多少条路径权值乘积不小于 $n$
$R,C \leq 300, n \leq 10^6$
sol:
暴力 dp 是 $O(R \times C \times n)$ 的
然后发现如果一条路径大于 $n$ ,直接把它设成 $n$ 即可,然后又发现 $\lfloor \frac{n}{i} \rfloor$ 只有 $O(\sqrt{n})$ 种取值,记录一下即可做到 $O(R \times C \times \sqrt{n})$
#include <bits/stdc++.h> #define Debug(x) cerr << #x << " = " << x << '\n' #define debug(x) cerr << #x << " = " << x #define TAB << " " #define EDL << "\n" #define LL long long #define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -f; for(; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } const int mod = 1e9 + 7, maxn = 510; int n, m, q, stk[15010], top, bl[1005010]; int a[maxn][maxn], dp[2][maxn][15010]; inline void mo(int &x) { if(x >= mod) x -= mod; if(x < 0) x += mod; } int main() { //freopen("mobitel.in","r",stdin); //freopen("mobitel.out","w",stdout); n = read(), m = read(), q = read(); q--; rep(i, 1, n) rep(j, 1, m) a[i][j] = read(); for(int l = 1, r; l <= q; l = r + 1) r = q / (q / l), stk[++top] = q / r, bl[q / r] = top; // Debug(top); stk[++top] = 0, bl[0] = top; int now = q; rep(i, 1, m) { now /= a[1][i], dp[1][i][bl[now]] = 1; //debug(i) TAB; debug(now) TAB; debug(a[1][i]) EDL; } //Debug(now); int cur = 1, last = 0; rep(i, 2, n) { last = cur, cur = cur ^ 1; memset(dp[cur], 0, sizeof(dp[cur])); //Debug(cur); rep(j, 1, m) { // assert(a[i][j] != 0); rep(k, 1, top) { //assert(a[i][j] != 0); dp[cur][j][bl[stk[k] / a[i][j]]] += dp[last][j][k]; mo(dp[cur][j][bl[stk[k] / a[i][j]]]); if(j != m) { dp[cur][j + 1][bl[stk[k] / a[i][j + 1]]] += dp[cur][j][k]; mo(dp[cur][j + 1][bl[stk[k] / a[i][j + 1]]]); } //debug(i) TAB; debug(j) TAB; debug(k) EDL; } } } cout << dp[cur][m][top] << endl; }