LC 6115. 统计理想数组的数目
https://leetcode.cn/contest/weekly-contest-301/problems/count-the-number-of-ideal-arrays/
这题如果直接上DP的话,那么时间复杂度会非常高:假如每个元素平均的因子数量是K的话,那么复杂度就是O(N * maxValue * K). 所以这题肯定需要某种数学解法。
class Solution:
def idealArrays(self, n: int, maxValue: int) -> int:
adj = [[] for _ in range(1 + maxValue)]
for i in range(1, maxValue + 1):
for j in range(i, maxValue + 1):
if j % i == 0:
adj[i].append(j)
dp = [[0] * (1 + maxValue) for _ in range(2)]
for i in range(1 + maxValue):
dp[0][i] = 1
now = 0
MOD = 10 ** 9 + 7
for _ in range(1, n):
for i in range(1 + maxValue):
dp[1 - now][i] = 0
for i in range(1, maxValue + 1):
for x in adj[i]:
dp[1 - now][x] += dp[now][i]
now = 1 - now
ans = sum(dp[now]) % MOD
return ans
- 我们假设结尾是X的理想数组
- 我们不要关心数组中每个数,而关心两个数之间倍数可能性
- 这个倍数可能是可以通过因子分解来构造。
假设X可以分解为 `X=(p1 * x1) … (pk * xk)` 的话,我们可以逐个质因子考虑
- p1出现了x1次,如果将x1次放在n个倍数之间,有多少种分布?假设是 K1
- 那么所有可能性就是 `(K1 * K2 * .. Kk)`
- 而将x1放在n个倍数之间分布数可以通过组合数学 `C(n+x1-1, x1)`
class Solution:
def idealArrays(self, n: int, maxValue: int) -> int:
# 质因子分解
ks = [[] for _ in range(maxValue + 1)]
for i in range(2, maxValue + 1):
k, x = 2, i
while k * k <= x:
if x % k == 0:
c = 0
while x % k == 0:
c += 1
x //= k
ks[i].append(c)
k += 1
if x > 1:
ks[i].append(1)
MOD = 10 ** 9 + 7
# 计算出C(n, k)
import functools
@functools.lru_cache(maxsize=None)
def comb(n, k):
if k == 0: return 1
if n == k: return 1
return (comb(n - 1, k - 1) + comb(n - 1, k)) % MOD
ans = 0
# 计算结尾为i的可能数
for i in range(1, maxValue + 1):
mul = 1
# 考虑每个质因子分布的可能性
for k in ks[i]:
mul = mul * comb(n + k - 1, k)
mul = mul % MOD
ans += mul
ans %= MOD
return ans