[Ignatius and the Princess III] 整数的无序拆分(DP + 生成函数)
整数的有序拆分就是隔板法,无序拆分则有两种处理方法
DP递推
-
我们假设是正整数无序拆分为个正整数的方案数
-
对于某一种拆分,不妨将拆分出来的个数从小到大排序,分类讨论
- 最小的数等于,那么去掉这个,相当于把剩下的拆分成个数,方案数就为
- 最下的数大于,那么将所有的数减去,相当于把剩下的拆分成个数,方案数就为
-
则最终答案为,时间复杂度为
-
AC code
#include <bits/stdc++.h> using namespace std; int P[121][121]; int main () { for(int i = 1; i <= 120; ++i) { P[i][1] = P[i][i] = 1; for(int j = 2; j < i; ++j) P[i][j] = P[i-1][j-1] + P[i-j][j]; } for(int i = 1; i <= 120; ++i) for(int j = 1; j <= i; ++j) //做前缀和 P[i][j] += P[i][j-1]; int n; while(~scanf("%d", &n)) printf("%d\n", P[n][n]); }
生成函数/卷积
- 想一想,显然可得答案为
所得多项式中次数为的系数 - 因为是多项式的乘积,就是在每个多项式中选项,最后再加起来。在第个多项式中,表示数不选,表示选了个
- 这实际上就是把加法运算,转化为多项式的次数来做乘法/卷积。思想类似于(分治)FFT等
- 时间复杂度为
-
AC code
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int MAXN = 121; int n, ans[MAXN], tmp[MAXN]; int main () { while(~scanf("%d", &n)) { for(int i = 0; i <= n; ++i) ans[i] = 1, tmp[i] = 0; for(int i = 2; i <= n; ++i) { for(int j = 0; j <= n; j+=i) for(int k = 0; k + j <= n; ++k) tmp[j+k] += ans[k]; for(int j = 0; j <= n; ++j) ans[j] = tmp[j], tmp[j] = 0; } printf("%d\n", ans[n]); } }