题解:P6189 [NOI Online #1 入门组] 跑步
题意:求 \(n\) 的分拆数。
做法:
经典问题,我们考虑直接对 \(< B = \sqrt n\) 和 \(\ge B\) 的数分类进行处理。
对于 \(<B\) 的数,显然就是一个完全背包,复杂度 \(O(n^{\frac 3 2})\)。
对于 \(\ge B\) 的数,我们只会选 \(O(B)\) 个,我们把图像画出来,发现是一个阶梯状物,这个东西就可以用很经典的计数方法。
我们考虑用以下两种操作生成一个阶梯状物:
-
加入一个长为 \(B\) 的条。
-
整体全部 \(+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;
}

浙公网安备 33010602011771号