[学习笔记]生成函数1
[学习笔记]生成函数1
参考资料
- 西安交大 唐宇轩在西安交大附中讲课的PPT
- 信息学竞赛数学一本通
形式幂级数
我们把形如 $ \sum_{i>=0}a_i\cdot x^{i} $ 的式子称为形式幂级数
形式幂级数是个长度无限的多项式,但对于有些形式幂级数可以用长度有限的函数来表示,称其为闭形式
生成函数
生成函数有很多种类,OI中常用的有普通生成函数和指数生成函数,即OGF和EGF。
生成函数就是一种形式幂级数,因此不需要考虑它是否收敛。
普通生成函数
定义数列A的普通生成函数为 $ A(x) = \sum_{i>=0}a_i\cdot x^{i} $
指数生成函数
定义数列A的指数生成函数为 $ A(x) = \sum_{i>=0}\frac{a_i}{i!}\cdot x^{i} $
生成函数是求解组合方面题目的一种重要方法
组合计数问题
组合计数问题是指,题目给了一些组合条件,然后还有一个用于衡量大小的参数 𝑠𝑖𝑧𝑒 ,我们要求满足给定组合条件并且 𝑠𝑖𝑧𝑒=𝑛 的方案数
组合计数问题通常根据内部元素是否有区别分为有标号和无标号两种
OGF用于解决无标号的组合计数问题,EGF用于解决有标号的组合计数问题
基本运算(加法原理和乘法原理)
假设现在有两个组合计数问题
对于组合计数问题一,设 $ 𝑎_𝑖 $ 表示 𝑠𝑖𝑧𝑒=𝑖 时满足组合条件的方案数
对于组合计数问题二,设 $ 𝑏_𝑖 $ 表示 𝑠𝑖𝑧𝑒=𝑖 时满足组合条件的方案数,设 𝐴,𝐵 分别为数列 𝑎,𝑏 的生成函数
- 加法原理
对于组合计数问题三,满足其组合条件的方法有两种,要么满足问题1的条件要么满足问题2的条件
那么设 𝑐_𝑖 表示 𝑠𝑖𝑧𝑒=𝑖 时满足问题三组合条件的方案数,设 𝐶 为数列 𝑐 的生成函数,则有 𝐶=𝐴+𝐵
换句话说对于两类组合对象A和B,若C为AB并集,则有C(x) = A(x) + B(x),时间复杂度为 O(n)
- 乘法原理
对于组合计数问题四,满足其组合条件的方法有两步,先满足问题一的条件,再满足问题二的条件
那么设 𝑑_𝑖 表示 𝑠𝑖𝑧𝑒=𝑖 时满足问题四组合条件的方案数,设 𝐷 为数列 𝑑 的生成函数,则有 𝐷=𝐴𝐵
换句话说对于两类组合对象A和B,若D是AB的笛卡尔积,即D中每个元素d都是A中某元素a与B中某元素b拼成的二元组 (a,b),其大小|d|定义为|a|+|b|。
D(x) = A(x)B(x) ,考虑FFT,O(nlogn)
如何用普通生成函数解决组合计数问题
例题1:有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?每种重量各有几种可能方案?
考虑构造如下生成函数G(x) = (1+x)(1+x^2)(1+x^3)+(1+x^4),其中1可以看做x^0
于是(1+x)中1可以看做不取1克,x^1可以看做取了1个1克砝码,同理(1+x^2)1可以看做不取2克,x^2可以看做取1个2克砝码,同理递推
讲G(x)展开G(x) = 1+x+x^2+2x^3+2x^4+2x^5+2x^6+2x^7+x^8+x^9+x^10
不难发现指数代表重量,而系数代表方案数
例题2 : HDU1028 "The second problem is, given an positive integer N, we define an equation like this: N=a[1]+a[2]+a[3]+...+a[m]; a[i]>0,1<=m<=N; My question is how many different equations you can find for a given N.
构造生成函数G(x) = (1 + x^1 + x^2 + ......)*(1 + x^2 + x^4 + x^6+....)*(1+x^3 + x^6 + x^9 + .......)*......
答案即为x^n的系数
代码如下:
#include<bits/stdc++.h> #define LL long long using namespace std; inline LL read() { LL f = 1 , x = 0; char ch; do { ch = getchar(); if(ch=='-') f=-1; } while(ch<'0'||ch>'9'); do { x=(x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch>='0'&&ch<='9'); return f*x; } const int MAXN = 100 + 50; int n; int a[MAXN],b[MAXN]; int main() { while(~scanf("%d",&n)) { for(register int i=0;i<=n;i++) { a[i] = 1,b[i] = 0; } for(register int i=2;i<=n;i++) { for(register int j=0;j<=n;j++) { for(register int k=0;k+j<=n;k+=i) { b[j+k]+=a[j]; } } for(register int j=0;j<=n;j++) { a[j] = b[j]; b[j] = 0; } } printf("%d\n",a[n]); } }