题解:P6189 [NOI Online #1 入门组] 跑步

题意:求 \(n\) 的分拆数。

做法:

经典问题,我们考虑直接对 \(< B = \sqrt n\)\(\ge B\) 的数分类进行处理。

对于 \(<B\) 的数,显然就是一个完全背包,复杂度 \(O(n^{\frac 3 2})\)

对于 \(\ge B\) 的数,我们只会选 \(O(B)\) 个,我们把图像画出来,发现是一个阶梯状物,这个东西就可以用很经典的计数方法。

我们考虑用以下两种操作生成一个阶梯状物:

  1. 加入一个长为 \(B\) 的条。

  2. 整体全部 \(+1\)

显然是可以覆盖所有的情况的。

那么我们记 \(dp_{i,j}\) 代表用了 \(i\) 个数,和为 \(j\) 有多少种方案,直接按上面转移即可。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e5 + 5, B = 400;
int n, mod, dp1[2][maxn], dp2[2][maxn], t[maxn];
signed main() {
	cin >> n >> mod;
	dp2[0][0] = 1;
	for (int i = 1; i < min(n + 1, B); i++) {
		for (int j = i; j <= n; j++)
			dp2[0][j] = (dp2[0][j] + dp2[0][j - i]) % mod;
//		for (int j = 1; j <= n; j++)
//			cout << dp2[0][j] << " ";
//		cout << endl;
	}
	dp1[0][0] = 1;
	t[0] = 1;
	for (int i = 1; i <= B; i++) {
		memcpy(dp1[1], dp1[0], sizeof(dp1[1]));
		memset(dp1[0], 0, sizeof(dp1[0]));
		for (int j = B; j <= n; j++)
			dp1[0][j] = (dp1[0][j - i] + dp1[1][j - B]) % mod;
		for (int j = 1; j <= n; j++)
			t[j] = (t[j] + dp1[0][j]) % mod;
	}
	int ans = 0;
	for (int i = 0; i <= n; i++)
		ans = (ans + dp2[0][i] * t[n - i]) % mod;
	cout << ans << endl;
	return 0;
}
posted @ 2025-07-25 17:15  LUlululu1616  阅读(17)  评论(0)    收藏  举报