普通型母函数详解及其模板类型

PS:还记得第一次接触普通型母函数(也就是生成函数)还是在离散数学的课本上,当时也不太会敲代码,仅限于能够手动计算,现在就要详细的介绍一下关于母函数的内容了:

普通型母函数又叫生成函数,

1.定义:设G(x) = a0 + a1*x + a2*x^2 + ... + an*x^n + ...,就称G(x) 是序列{an}的生成函数;

Eg:{C(n,m)}的生成函数是 (1+x)^m,{k^n}的生成函数是 G(x) = 1+k*x+k^2*x^2+.. == 1/(1-k*x)


2.基本模型:

1.砝码问题:

假设现在有1克砝码2个,2克砝码1个,4克砝码2个,问能够称出哪些重量,方案数有多少?

解:

可以列出不定方程:

1*x1 + 2*x2 + 4*x3 == r;(0<=x1<=2, 0<=x2<=1, 0<=x3<=2)

所以对应的母函数为:

G(x) = (1+x+x^2) * (1+x^2) * (1+x^4+x^8) = 1+x+2*x^2+x^3+2*x^4+x^5+2*x^6+x^7+2*x^8+x^9+2*x^10+x^11+x^12

其中 x 的指数就是能够称的重量,而 x 前面的系数就是能够成重量是 指数的方案数;

2.正整数拆分问题:

G(x) = (1+x+x^2+x^3+x^4+...)* (1+x+x^2+x^3+x^4+...)* (1+x+x^2+x^3+x^4+...)*...*(乘以n次)

3.取小球问题:

口袋中有白球2个,红球3个,黄球1个,从袋中摸出3个球有几种取法?

G(x)= (1+x+x^2) * (1+x+x^2+x^3) * (1+x)

1表示不取球,x代表取1个球,x^2代表取2个球, x^3代表取3个球

所以我们只需要求出x^3前面的系数就行了,因为X^m前面的系数表示的就是摸出 m 个球所需要的方法数;

4.放球问题:

有n个球放到m个盒子中,有多少种不同的放法?

G(x) = (1+x+x^2+x^3+...x^k+...)*(1+x+x^2+x^3+...x^k+...)*...(一共有 n 项)我们要求的就是x^m前面的系数,也就是方法数;

5.就是求的 R-组合数

Eg:求 S = ( 3 * a, 4 * b, 5 * c)的10-组合数N;

解:生成函数G(x) = (1+x+x^2+x^3) * (1+x+x^2+x^3+x^4) * (1+x+x^2+x^3+x^4+x^5)

                             = ( 1 + ... + 3*x^10+2*x^10+x^10+...)

所以 x^10的系数就是6,所以 N = 6;


***************************************分割线***********************************************

介绍完这些基本例子之后就是说一下基本的模板了,也就是写程序:

拿整数拆分那个来说吧,其实程序大多数都是一样的,没有什么太大的差别:大体上就是利用两个数组 c1[],c2[],c1数组就是用来存多项式各项系数最后结果的,而c2数组就是用来中间转换的,当每次计算结束后,把它赋给c1,然后c2清零。然后3重循环:最外层,记录它和第几个多项式相乘;第二层,表示c1中的每一项;第三层表示后面被乘多项式中的每一项。PS:先进行手工做一下,在模拟一下应该就行了,注意理解


My Code:


<span style="font-size:18px;">#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
#include <set>
using namespace std;

#define MM(a) memset(a,0,sizeof(a))

typedef long long LL;
typedef unsigned long long ULL;
const int maxn = 1e6+5;
const int mod = 1e9+7;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
LL gcd(LL a, LL b)
{
    if(b == 0)
        return a;
    return gcd(b, a%b);
}
int c1[maxn];///每一次存的多项式中的数
int c2[maxn];///中间变量
int n;///拆分的数

/**
首先对c1初始化,由第一个表达式
(1+x+x^2+..x^n)初始化,
从0到n的所有x前面的系数都初始化为1.
**/
void Init()
{
    for(int i=0; i<=n; i++)
    {
        c1[i] = 1;
        c2[i] = 0;
    }
}

int main()
{
    while(cin>>n)
    {
        Init();
        for(int i=2; i<=n; i++)///控制一共循环几次,也就是一共有几个多项式相乘
        {
            for(int j=0; j<=n; j++)///第一个表达式的系数
            {
                for(int k=0; k+j<=n; k+=i)///我们只需要 n 前面的系数
                {
                    c2[k+j] += c1[j];///重点理解,只要这个理解明白了就行了
                }
            }
            for(int j=0; j<=n; j++)///我们在用 c1[]当作第一个乘
            {
                c1[j] = c2[j];
                c2[j] = 0;///c2不需要
            }
        }
        cout<<c1[n]<<endl;
    }
    return 0;
}
</span>


上一篇:专家支招东莞:用工业大数据驱动智能制造


下一篇:简单巴什博弈