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>