1 条题解

  • 0
    @ 2025-12-2 10:57:17

    题意概述

    给定一个 r×sr \times s 的矩阵,每个格子有一个正整数。
    从左上角 (1,1)(1,1) 走到右下角 (r,s)(r,s),每次只能向右或向下走。
    定义一条路径的权值为路径上所有格子数值的乘积。
    求权值不小于 nn 的路径条数,答案对 109+710^9+7 取模。

    数据范围:r,s300r,s \le 300n106n \le 10^6,矩阵元素 106\le 10^6


    核心难点

    1. 路径数:从 (1,1)(1,1)(r,s)(r,s) 的路径总数是 (r+s2r1)\binom{r+s-2}{r-1},最大约 109010^{90} 量级,无法直接枚举。
    2. 乘积增长:路径乘积可以极大(300300 个数乘积可能达到 (106)300=101800(10^6)^{300} = 10^{1800}),无法直接存储。
    3. 约束:要求乘积 n\ge n,不是模意义下的比较,而是真实数值比较。

    关键观察

    由于 n106n \le 10^6,我们可以将“乘积不小于 nn”转化为:
    设路径乘积为 PP,如果 PnP \ge n,则 n1P=0\lfloor \frac{n-1}{P} \rfloor = 0,否则 1\ge 1
    但这并不利于直接 DP。

    更直接的想法:
    乘积 PP 可以写成 k×qk \times q 形式,其中 k=gcd(P,n)k = \gcd(P, n) 等,但更常用技巧是:


    转化为对数比较

    取对数:logP=logai,j\log P = \sum \log a_{i,j}
    条件 PnP \ge n 等价于 logPlogn\log P \ge \log n

    由于 ai,j106a_{i,j} \le 10^6logai,jlog10613.8\log a_{i,j} \le \log 10^6 \approx 13.8,路径最多 600600 步,logP8280\log P \le 8280,可用浮点数或定点数存储。
    但浮点数有精度问题,可能影响比较。


    更稳健的方法:质因数分解

    因为 n106n \le 10^6,可以将其质因数分解。
    n=p1e1p2e2pmemn = p_1^{e_1} p_2^{e_2} \dots p_m^{e_m}
    对于路径乘积 PP,要满足 PnP \ge n,等价于:
    对每个质因子 ppPPpp 的指数不小于 nnpp 的指数。

    不对! 因为即使每个质因子的指数都达到要求,PP 还可能比 nn 小(比如其他质因子指数为0,但 nn 有其他因子?实际上 PP 包含所有 nn 的质因子且指数足够时,PP 一定是 nn 的倍数,所以 PnP \ge n 成立)。
    更准确:PnP \ge n 等价于 PPnn 的倍数,且 P/n1P/n \ge 1,但 P/nP/n 可能不是整数?不,若 PPnn 的倍数,则 P/n1P/n \ge 1 自动成立。

    因此等价条件是:
    PPnn 的倍数。

    因为如果 PPnn 的倍数,则 PnP \ge n(因为矩阵元素是正整数)。
    反过来,如果 PnP \ge n,不一定 PPnn 的倍数。
    所以这个转化是错的,不能直接等价。


    正确转化:阈值法

    nn 的质因数分解为 n=piein = \prod p_i^{e_i}
    对于 PP,设它在 pip_i 上的指数为 fif_i,则: [ P \ge n \iff \prod p_i^{\max(0, f_i - e_i)} \times \text{(其他质因子幂)} \ge 1 ] 这并不好直接处理。

    常用技巧:
    T=n1T = n-1,那么 PnP \ge n 等价于 T/P=0\lfloor T/P \rfloor = 0
    PP 可能很大,不能直接除。


    动态规划状态设计

    dp[i][j][v]dp[i][j][v] 表示走到 (i,j)(i,j) 时,路径乘积为 vv 的路径数。
    vv 可能极大,不能直接存。

    注意到我们只关心 vv 是否 n\ge n,以及 vvnn 的比值在某种意义下的状态。
    因为 n106n \le 10^6,我们可以考虑存储 vvnn 的值?不行,模 nn 不能判断大小。

    另一个思路:
    g=gcd(v,n)g = \gcd(v, n),并存储 v/gv/g 的某些信息?
    v=g×tv = g \times t,其中 gng \mid n,且 gcd(t,n)=1\gcd(t, n) = 1
    那么 vnv \ge n 等价于 tn/gt \ge n/g


    关键突破:状态为 (i, j, k)

    定义 k=n1vk = \lfloor \frac{n-1}{v} \rfloor
    这里 vv 是路径乘积。
    kk 的范围是 00n1n-1
    vnv \ge n 时,k=0k = 0;否则 k1k \ge 1

    转移时,从 (i,j)(i,j)(i+1,j)(i+1,j)(i,j+1)(i,j+1)vv 乘以 ai,ja_{i,j},对应 kk 变为 n1v×a\lfloor \frac{n-1}{v \times a} \rfloor
    这个转移可以写为: [ k' = \left\lfloor \frac{k}{a} \right\rfloor \quad \text{? 验证:} ] 因为 k=n1vk = \lfloor \frac{n-1}{v} \rfloor,所以 [ k' = \left\lfloor \frac{n-1}{v \cdot a} \right\rfloor = \left\lfloor \frac{\lfloor \frac{n-1}{v} \rfloor}{a} \right\rfloor ] 这是正确的吗?
    有公式 $\lfloor \frac{\lfloor x \rfloor}{a} \rfloor = \lfloor \frac{x}{a} \rfloor$ 当 aa 是正整数时成立。
    因此: [ k' = \left\lfloor \frac{k}{a} \right\rfloor ] 成立。

    因此,我们只需要 DP 状态 (i,j,k)(i,j,k),其中 k=n1vk = \lfloor \frac{n-1}{v} \rfloorvv 是当前乘积。
    初态:(1,1,n1a1,1)(1,1, \lfloor \frac{n-1}{a_{1,1}} \rfloor)
    终态:(r,s,0)(r,s,0) 的路径数即为所求(因为 k=0k=0vnv \ge n)。


    DP 转移

    dp[i][j][k]dp[i][j][k] 表示走到 (i,j)(i,j)k=n1vk = \lfloor \frac{n-1}{v} \rfloor 的路径数。
    转移: [ dp[i+1][j][\lfloor k/a_{i+1,j} \rfloor] \ += dp[i][j][k] ] [ dp[i][j+1][\lfloor k/a_{i,j+1} \rfloor] \ += dp[i][j][k] ] 所有运算在模 109+710^9+7 下进行。


    复杂度

    状态数:r×s×nr \times s \times n,最大 300×300×106300 \times 300 \times 10^6,显然太大。
    需要优化。

    注意 kk 的取值:
    初始 kn1k \le n-1,每次转移 kk 变为 k/a\lfloor k/a \rflooraa 是矩阵元素。
    因为 a1a \ge 1kk 会快速减小。
    实际上,kk 的不同取值个数是 O(n)O(\sqrt{n}) 级别?
    更准确:经过多次除以 aaa2a \ge 2),kk 很快到 0。
    但最坏情况 a=1a=1kk 不变。
    所以当 a=1a=1 时,kk 不变,状态可能很多。


    优化:只存有效状态

    用 map 或 unordered_map 存储每个 (i,j)(i,j) 对应的 kk 的分布。
    由于 kk 取值是整数除法下递降的,不同 kk 的数量是 O(n)O(\sqrt{n}) 级别(实际上更少,因为每次除以至少 1,且矩阵元素不一定全为 1)。

    r,s300r,s \le 300n106n \le 10^6,最坏情况 ai,j=1a_{i,j}=1kk 始终为 n1n-1,这样每个 (i,j)(i,j) 只有一个 kk 值,总状态数 300×300=9×104300\times 300 = 9\times 10^4,可以接受。
    如果 aa 较大,kk 会迅速减小,状态更少。

    所以用 unordered_map 存 kcountk \to count 映射,总状态数可接受。


    算法步骤

    1. 读入 r,s,nr,s,n 和矩阵 aa
    2. 初始化:dp[1][1][(n1)/a[1][1]]=1dp[1][1][ \lfloor (n-1)/a[1][1] \rfloor ] = 1
    3. 按行、列顺序遍历 (i,j)(i,j),对每个 dp[i][j]dp[i][j] 中的每个 (k,cnt)(k, cnt)
      • 向右走:nk=k/a[i][j+1]nk = \lfloor k / a[i][j+1] \rfloordp[i][j+1][nk]+=cntdp[i][j+1][nk] += cnt
      • 向下走:nk=k/a[i+1][j]nk = \lfloor k / a[i+1][j] \rfloordp[i+1][j][nk]+=cntdp[i+1][j][nk] += cnt
    4. 最终答案 = dp[r][s][0]dp[r][s][0]

    复杂度分析

    每个 (i,j)(i,j)kk 的不同取值个数是 O(n)O(\sqrt{n}) 级别,但实际更小。
    总状态数约 O(rsn)O(rs\sqrt{n}),最坏 300×300×1000108300\times 300\times 1000 \approx 10^8,可能稍大,但很多 kk 会快速变为 0,实际运行可过。


    总结

    本题核心是将“乘积不小于 nn”转化为 (n1)/P=0\lfloor (n-1)/P \rfloor = 0,并利用整数除法的性质设计 DP 状态 k=(n1)/Pk = \lfloor (n-1)/P \rfloor,转移为 k=k/ak' = \lfloor k/a \rfloor
    通过只存储有效状态,将巨大的乘积范围压缩到 0n10 \sim n-1 的整数,实现了可行解法。
    复杂度 O(rsS)O(rs \cdot S),其中 SSkk 的不同取值个数,实际运行较快。

    • 1

    信息

    ID
    5741
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者