[Ignatius and the Princess III] 整数的无序拆分(DP + 生成函数)

整数的有序拆分就是隔板法,无序拆分则有两种处理方法

DP递推

  • 我们假设P(n,m)P(n,m)是正整数nn无序拆分为mm个正整数的方案数

  • 对于某一种拆分,不妨将拆分出来的mm个数从小到大排序,分类讨论

    • 最小的数等于11,那么去掉这个11,相当于把剩下的n1n-1拆分成m1m-1个数,方案数就为P(n1,m1)P(n-1,m-1)
    • 最下的数大于11,那么将所有的数减去11,相当于把剩下的nmn-m拆分成mm个数,方案数就为P(nm,m)P(n-m,m)
  • 则最终答案为i=1nP(n,i)\large\sum_{i=1}^nP(n,i),时间复杂度为Θ(n2)\large \Theta(n^2)

  • 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]);
    }
    

生成函数/卷积

  • 想一想,显然可得答案为
    (1+x1+x2+...)(1+x2+x4+...)(1+x3+x6+...)...\large (1+x^1+x^2+...)\\*(1+x^2+x^4+...)\\*(1+x^3+x^6+...)\\*...所得多项式中次数为nn的系数
  • 因为是多项式的乘积,就是在每个多项式中选11项,最后再加起来。在第ii个多项式中,11表示数ii不选,xkix^{ki}表示选了kkii
  • 这实际上就是把加法运算,转化为多项式的次数来做乘法/卷积。思想类似于(分治)FFT等
  • 时间复杂度为Θ(n3)\large \Theta(n^3)
  • 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]);
    	}
    }
    
posted @ 2019-12-14 14:52  _Ark  阅读(212)  评论(0编辑  收藏  举报