【SSL 1479】不知道该叫啥

题目大意:

求有多少长度为 \(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\times j\leq M\),那 \(i\leq \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;
}
posted @ 2020-08-14 17:14  Jayun  阅读(168)  评论(0编辑  收藏  举报