给你两个整数 n 和 maxValue ,用于描述一个 理想数组 。
对于下标从 0 开始、长度为 n 的整数数组 arr ,如果满足以下条件,则认为该数组是一个 理想数组 :
每个 arr[i] 都是从 1 到 maxValue 范围内的一个值,其中 0 <= i < n 。
每个 arr[i] 都可以被 arr[i - 1] 整除,其中 0 < i < n 。
返回长度为 n 的 不同 理想数组的数目。由于答案可能很大,返回对 10 9 + 7 10^9 + 7 1 0 9 + 7 取余的结果。
示例 1:
输入 :n = 2, maxValue = 5
输出 :10
解释 :存在以下理想数组:
以 1 开头的数组(5 个):[1,1]、[1,2]、[1,3]、[1,4]、[1,5]
以 2 开头的数组(2 个):[2,2]、[2,4]
以 3 开头的数组(1 个):[3,3]
以 4 开头的数组(1 个):[4,4]
以 5 开头的数组(1 个):[5,5]
共计 5 + 2 + 1 + 1 + 1 = 10 个不同理想数组。
示例 2:
输入 :n = 5, maxValue = 3
输出 :11
解释 :存在以下理想数组:
以 1 开头的数组(9 个):
不含其他不同值(1 个):[1,1,1,1,1]
含一个不同值 2(4 个):[1,1,1,1,2], [1,1,1,2,2], [1,1,2,2,2], [1,2,2,2,2]
含一个不同值 3(4 个):[1,1,1,1,3], [1,1,1,3,3], [1,1,3,3,3], [1,3,3,3,3]
以 2 开头的数组(1 个):[2,2,2,2,2]
以 3 开头的数组(1 个):[3,3,3,3,3]
共计 9 + 1 + 1 = 11 个不同理想数组。
提示:
问题分析
注意到 :
任意理想数组 a i {a_i} a i 满足 1 ≤ a 0 ∣ a 1 ∣ ⋯ ∣ a n − 1 = y , a i ≤ maxValue 1\le a_0\mid a_1\mid\cdots\mid a_{n-1}= y,\text{ }a_i\le \text{maxValue} 1 ≤ a 0 ∣ a 1 ∣ ⋯ ∣ a n − 1 = y , a i ≤ maxValue 。
对于固定的末尾值 y ≤ m a x V a l u e y\le\mathrm{maxValue} y ≤ maxValue ,从 1 变到 y y y 的“除数链”可看作在每个质因数方向上“累积指数”的过程(把它的每个质因子 p p p 、指数 e p e_p e p 看成要在前 n − 1 n-1 n − 1 个“空位”中放 e p e_p e p 个“乘 p p p ”操作)。
若 y = ∏ p i e i y=\prod p_i^{e_i} y = ∏ p i e i ,则在长度为 n n n 的链中,需要分配这 e i e_i e i 次“乘以 p i p_i p i ”操作到 n − 1 n-1 n − 1 个相邻位置上,属于 「Stars and Bars」 模型( “星星与条纹” ),每种质因子独立,所以总方案数是:
# { 链数 } = ∏ i ( ( n − 1 ) + e i e i ) . \#\{\text{链数}\}=\prod_i\binom{(n-1)+e_i}{e_i}.
# { 链数 } = i ∏ ( e i ( n − 1 ) + e i ) .
最后对所有 y y y 累加,并对 10 9 + 7 10^9+7 1 0 9 + 7 取模,因此,答案就是
∑ y = 1 m a x V a l u e [ ∏ p e ∥ y ( ( n − 1 ) + e e ) ] m o d ( 10 9 + 7 ) . \sum_{y=1}^\mathrm{maxValue}\left[\prod_{p^e\|y}\binom{(n-1)+e}{e}\right]\mathrm{mod}(10^9+7).
y = 1 ∑ maxValue p e ∥ y ∏ ( e ( n − 1 ) + e ) mod ( 1 0 9 + 7 ) .
预处理 → 枚举 y=1…maxValue → 分解 y 的质因子 → 用“comb”累乘 → 累加取模 → 返回
算法思路
预处理阶乘与逆元阶乘 ,支持快速计算组合数 ( a b ) \binom{a}{b} ( b a ) 。
用 线性/筛法 (SPF 最小质因子)对 [ 1 … m a x V a l u e ] [1\ldots\mathrm{maxValue}] [ 1 … maxValue ] 做一次质因数分解,整体复杂度约 O ( m a x V a l u e log log m a x V a l u e ) O(\mathrm{maxValue}\log\log \mathrm{maxValue}) O ( maxValue log log maxValue ) 。
对每个 y y y 枚举其质因数指数 e i e_i e i ,累乘组合数即可。
时间复杂度
阶乘 & 逆元:O ( n + log m a x V a l u e ) O(n + \log \mathrm{maxValue}) O ( n + log maxValue )
SPF 筛:O ( max V a l u e log log m a x V a l u e ) O(\max\mathrm{Value}\log\log\mathrm{maxValue}) O ( max Value log log maxValue )
主循环:每个 y y y 仅分解一次,总体 O ( max V a l u e log m a x V a l u e ) O(\max\mathrm{Value}\log\mathrm{maxValue}) O ( max Value log maxValue )
总体 :O ( n + max V a l u e log log m a x V a l u e + max V a l u e log m a x V a l u e ) O(n+\max\mathrm{Value}\log\log\mathrm{maxValue}+\max\mathrm{Value}\log\mathrm{maxValue}) O ( n + max Value log log maxValue + max Value log maxValue ) ,低于DP
空间复杂度:O ( n + max V a l u e ) O(n+\max\mathrm{Value}) O ( n + max Value ) ,主要用于阶乘数组与 SPF 数组。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import mathclass Solution : def idealArrays (self, n: int , maxValue: int ) -> int : MOD = 10 **9 + 7 maxE = int (math.log2(maxValue)) + 1 up = n - 1 + maxE fact = [1 ] * (up + 1 ) invfact = [1 ] * (up + 1 ) for i in range (1 , up + 1 ): fact[i] = fact[i-1 ] * i % MOD invfact[up] = pow (fact[up], MOD-2 , MOD) for i in range (up, 0 , -1 ): invfact[i-1 ] = invfact[i] * i % MOD def comb (a: int , b: int ) -> int : if b < 0 or b > a: return 0 return fact[a] * invfact[b] % MOD * invfact[a-b] % MOD spf = list (range (maxValue + 1 )) for p in range (2 , int (maxValue**0.5 ) + 1 ): if spf[p] == p: for j in range (p*p, maxValue + 1 , p): if spf[j] == j: spf[j] = p ans = 0 for y in range (1 , maxValue + 1 ): ways = 1 v = y while v > 1 : p = spf[v] e = 0 while v % p == 0 : v //= p e += 1 ways = ways * comb((n-1 ) + e, e) % MOD ans = (ans + ways) % MOD return ans
另看到题解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Solution : def idealArrays (self, n: int , maxValue: int ) -> int : MOD = 10 **9 + 7 maxE = int (math.log2(maxValue)) + 1 up = n - 1 + maxE C = [[0 ] * (maxE + 1 ) for _ in range (up + 1 )] for i in range (up + 1 ): C[i][0 ] = 1 for j in range (1 , min (i, maxE) + 1 ): C[i][j] = (C[i-1 ][j] + C[i-1 ][j-1 ]) % MOD EXP = [[] for _ in range (maxValue + 1 )] for y in range (2 , maxValue + 1 ): v = y p = 2 while p * p <= v: e = 0 while v % p == 0 : v //= p e += 1 if e: EXP[y].append(e) p += 1 if v > 1 : EXP[y].append(1 ) ans = 0 for y in range (1 , maxValue + 1 ): ways = 1 for e in EXP[y]: ways = ways * C[n - 1 + e][e] % MOD ans = (ans + ways) % MOD return ans
DP 转组合 :把「每一步都要保持可整除」的递推,转化为「质因子指数如何在 n n n 个位置上非负分配」的组合计数;
枚举 :对每个可能的末尾值 x x x 计算它的组合数贡献,再求和。
对于固定的 y y y ,它的每个质因子 p p p 在 a 0 , … , a n − 1 a_0,\dots,a_{n-1} a 0 , … , a n − 1 中的指数序列
e 0 = 0 ( 不一定为0 ) ≤ e 1 ≤ ⋯ ≤ e n − 1 = E , e_0=0(\text{不一定为0})\le e_1\le\cdots\le e_{n−1}=E,
e 0 = 0 ( 不一定为 0 ) ≤ e 1 ≤ ⋯ ≤ e n − 1 = E ,
且 ∑ \sum ∑ 个位置上的“增加量”恰好等于 E E E ( e 1 + e 2 + . . . + e k = E e_1 + e_2 + ... + e_k = E e 1 + e 2 + ... + e k = E )(即 y y y 中该质因子的总指数)。
“星星与条纹” :前文已解释
实现
预处理每个 x ≤ maxValue x≤\text{maxValue} x ≤ maxValue 的质因数指数列表 ,EXP[x] 存放 x x x 分解后,每个质因子的指数 [ e 1 , e 2 , … ] [e_1,e_2,\dots] [ e 1 , e 2 , … ] 。
预计算组合数 ( N k ) \binom{N}{k} ( k N ) :由于 n ≤ 10 4 n\le10^4 n ≤ 1 0 4 , 指数 E p E_p E p 也最多 log 2 ( 10 4 ) ≈ 14 \log_2(10^4)\approx14 log 2 ( 1 0 4 ) ≈ 14 ,我们只需算到 N = n + 14 − 1 N=n+14-1 N = n + 14 − 1 。
用帕斯卡三角,这样 C[N][k] 就是 ( N k ) m o d ( 10 9 + 7 ) \binom{N}{k}\bmod(10^9+7) ( k N ) mod ( 1 0 9 + 7 ) 。
枚举所有 x = 1 … m a x V a l u e x=1…maxValue x = 1 … ma x Va l u e ,累加它们作为数组末尾的方案数,注意 x = 1 x=1 x = 1 时 EXP[1] 为空,res=1,对应 “全 1 数组” 这一类。
复杂度:
质因数分解 :∑ x = 1 M x = O ( M 3 / 2 ) \sum_{x=1}^{M}\sqrt{x}=O(M^{3/2}) ∑ x = 1 M x = O ( M 3/2 ) ,这里 M = maxValue ≤ 10 4 M=\text{maxValue}\le10^4 M = maxValue ≤ 1 0 4 足够快。
组合数预处理 :O ( ( n + max E ) × max E ) ≈ O ( n ⋅ log M ) O\bigl((n+\max E)\times \max E\bigr)\approx O(n\cdot\log M) O ( ( n + max E ) × max E ) ≈ O ( n ⋅ log M ) 。
枚举累加 :O ( M × ( 平均质因子数 ) ) ≈ O ( M log M ) O\bigl(M\times (\text{平均质因子数})\bigr)\approx O(M\log M) O ( M × ( 平均质因子数 ) ) ≈ O ( M log M ) 。
特性
第一个实现(阶乘 + 逆元 comb + SPF 筛分解)
第二个实现(Pascal 组合表 + 试除分解)
组合数计算
预计算 fact 和 invfact 数组,用公式 ( a b ) = fact [ a ] fact [ b ] × fact [ a − b ] m o d p \binom{a}{b} = \frac{\text{fact}[a]}{\text{fact}[b] \times \text{fact}[a - b]} \mod p ( b a ) = fact [ b ] × fact [ a − b ] fact [ a ] mod p 每次 O ( 1 ) O(1) O ( 1 ) 快速计算
用帕斯卡三角预计算 C [ i ] [ j ] = ( i j ) C[i][j] = \binom{i}{j} C [ i ] [ j ] = ( j i ) 查询时直接 O ( 1 ) O(1) O ( 1 ) 取表
质因子分解
先用线性/埃氏思路构造 SPF(最小质因子)表 s p f [ i ] spf[i] s p f [ i ] 分解时不断 v / / = s p f [ v ] v //= spf[v] v // = s p f [ v ] ,复杂度约 O ( log v ) O(\log{v}) O ( log v )
每个 x x x 用试除法 x \sqrt{x} x 搜索素因子,累计其指数
预处理时间
构造 fact, invfact:O ( n + max E ) O(n + \max E) O ( n + max E ) (再加一次快速幂取逆) 构造 SPF:O ( M log log M ) O(M \log \log M) O ( M log log M )
构造 C C C 表:O ( ( n + max E ) × max E ) O\bigl((n + \max E) \times \max E\bigr) O ( ( n + max E ) × max E ) 试除分解:∑ x = 1 M O ( x ) ≈ O ( M 3 / 2 ) \sum_{x=1}^M O(\sqrt{x}) \approx O(M^{3/2}) ∑ x = 1 M O ( x ) ≈ O ( M 3/2 )
空间开销
两个长度 n + max E n + \max E n + max E 的数组 + 一个长度 M M M 的 SPF 数组
C C C 表大约 ( n + max E ) × ( max E ) (n + \max E) \times (\max E) ( n + max E ) × ( max E ) 个整数
适用场景
当 max V a l u e \max{Value} max Va l u e 更大(比如 10 5 10^5 1 0 5 、10 6 10^6 1 0 6 ) 时,SPF 分解会更快、更稳,阶乘逆元计算组合数也更节省空间
n , max V a l u e ≤ 10 4 n, \max{Value} \le 10^4 n , max Va l u e ≤ 1 0 4 都较小,预先填表和试除都能在 1–2s 内完成
代码复杂度
略微复杂一些,需要处理逆元和 SPF 数组
直观易懂,组合表和试除都很朴素
Qwen QWQ 32b给出的解,思路大致相同
(预处理最小质因数 → 质因数分解存储 → 阶乘与逆元预计算 → DP到组合数学 → 前缀和优化):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 class Solution : def idealArrays (self, n: int , maxValue: int ) -> int : MOD = 10 **9 + 7 t = n - 1 if t == 0 : return maxValue % MOD max_num = maxValue min_prime = [0 ] * (max_num + 1 ) for i in range (2 , max_num + 1 ): if min_prime[i] == 0 : min_prime[i] = i for j in range (i*i, max_num+1 , i): if min_prime[j] == 0 : min_prime[j] = i factor_list = [{} for _ in range (max_num + 1 )] for m in range (2 , max_num + 1 ): num = m factors = {} while num > 1 : p = min_prime[num] cnt = 0 while num % p == 0 : cnt += 1 num //= p factors[p] = cnt factor_list[m] = factors.copy() if factors else {} N_fact = 2 * 10 **5 fact = [1 ] * (N_fact + 1 ) for i in range (1 , N_fact + 1 ): fact[i] = (fact[i-1 ] * i) % MOD inv_fact = [1 ]*(N_fact + 1 ) inv_fact[N_fact] = pow (fact[N_fact], MOD -2 , MOD) for i in range (N_fact -1 , -1 , -1 ): inv_fact[i] = (inv_fact[i+1 ] * (i+1 )) % MOD fm = [0 ]*(max_num + 1 ) for m in range (1 , max_num + 1 ): if m == 1 : factors_dict = {} else : factors_dict = factor_list[m] product = 1 for p in factors_dict: a_i = factors_dict[p] n_val = a_i + t - 1 k_val = t - 1 if k_val > (n_val - k_val): k_val = n_val - k_val c_n_k = fact[n_val] * inv_fact[k_val] % MOD c_n_k = c_n_k * inv_fact[n_val - k_val] % MOD product = (product * c_n_k) % MOD fm[m] = product prefix = [0 ]*(max_num + 1 ) for i in range (1 , max_num+1 ): prefix[i] = (prefix[i-1 ] + fm[i]) % MOD res = 0 for a_val in range (1 , maxValue + 1 ): m_max = maxValue // a_val res += prefix[m_max] res %= MOD return res