题目大意:
求有多少长度为 (n) 的正整数序列,满足任意两个相邻的数的乘积不超过 (M)。
正文:
考虑用动态规划,设 (f_{i,j}) 表示第 (i) 位填 (j) 时的方案数,则转移方程是:
[f_{i,j}=sum_{k=1}^{frac{M}{j}}f_{i-1,k}
]
将状态滚成一维再用前缀和:
[f_j=sum_{frac{M}{j}}
]
对其进行整除分块(分个段)就可以快速预处理出前缀和 (sum_j=sum_{j-1}+len),(len) 值为当前一段的长度,最后转移方程为当前段长度乘上当前段价值。我们无法直接知道当前段的价值,而因为两点 (i,j) 若 (i imes jleq M),那 (ileq frac{M}{j}),既然 (i,j) 是一一对应的(可互换),所以一定在段数减去 (j) 加一里,当前段价值也就知道了。
代码:
int main()
{
scanf ("%d%d", &n, &m);
a[1] = m;
for (register int l = 1, r; l <= m; l = r + 1)
{
r = m / (m / l);
len[++i] = min(r - l + 1, m - l + 1),
sum[i] = sum[i - 1] + len[i];
}
for (int k = 2; k <= n; k++)
{
for (int j = 1; j <= i; j++)
f[j] = sum[i - j + 1] * len[j] % mod;
for (int j = 1; j <= i; j++)
sum[j] = (sum[j - 1] + f[j]) % mod;
}
printf("%lld", sum[i]);
return 0;
}