普通生成函数
引入
生成函数(generating function),又称母函数,是一种形式幂级数,其每一项的系数可以提供关于这个序列的信息,是解决排列组合问题的有力工具。
- Q: 什么叫做形式幂级数?
- A: 若某个生成函数为 \(F(x) = \sum a_n x^n\),那么其中的 \(x\) 对其取值范围并不是重点,其充当形式参数,没有实际意义,所以我们也不关心敛散性,而 \(a_n\) 才是我们需关注的重点。
本文主要探讨的是普通生成函数(ordinary generating function,OGF)。
前置知识
如下符号需要先感知:
\([x^i]f(x)\)
- 表示多项式 \(f(x)\) 在 \(x^i\) 处的系数
- 若其中的一项为 \(2023x^3\),那么 \([x^3]f(x) = 2023\)
下降幂
- \(x\) 的 \(n\) 阶下降幂为 \(x^{\underline{n}} = x(x - 1)\cdots(x - n + 1)\)
- 组合数的分子也恰好为这个形式
\(\dbinom{n}{i}\)
- 即组合数 \(C_n^i\)
- 有的地方也写为下降幂的形式,即 \(\frac{n^{\underline{i}}}{i!}\)
\(\sum_{i \geq 0}\)
- 等价于 \(\sum_{i=0}^{\infty}\),这类求和默认右边无穷,数均为整数
如下定理需要先感知:
二项式定理
\((x + y)^n = \sum_{i=0}^n \dbinom{n}{i} x^{n - i} y^i\),其中 \(\dbinom{n}{i} = \frac{n!}{i!(n - i)!}\) 是组合数。
广义二项式定理
\((x + y)^{\alpha} = \sum_{i=0}^{\infty} \dbinom{\alpha}{i} x^{\alpha - i} y^i\)
- 其中 \(\alpha\) 可以为一切实数,也就是说其可以为负数和浮点数
- 其中 \(\dbinom{\alpha}{i} = \frac{\alpha^{\underline{i}}}{i!} = \frac{\alpha(\alpha - 1)(\alpha - 2)\cdots(\alpha - i + 1)}{i!}\),可以理解为在实数域上的组合数。
定义
序列 \(a\) 的普通生成函数(ordinary generating function,OGF)定义为形式幂级数:
\(a\) 既可以是有穷序列,也可以是无穷序列。常见的例子(假设 \(a\) 以 \(0\) 为起点):
- 序列 \(a=\langle 1,2,3\rangle\) 的普通生成函数是 \(1+2x+3x^2\)。
- 序列 \(a=\langle 1,1,1,\cdots\rangle\) 的普通生成函数是 \(\sum_{n\ge 0}x^n\)。
- 序列 \(a=\langle 1,2,4,8,16,\cdots\rangle\) 的生成函数是 \(\sum_{n\ge 0}2^nx^n\)。
- 序列 \(a=\langle 1,3,5,7,9,\cdots\rangle\) 的生成函数是 \(\sum_{n\ge 0}(2n+1)x^n\)。
换句话说,如果序列 \(a\) 有通项公式,那么它的普通生成函数的系数就是通项公式。
普通生成函数的基本运算
考虑两个序列 \(a,b\) 的普通生成函数,分别为 \(F(x),G(x)\)。那么有
因此 \(F(x)\pm G(x)\) 是序列 \(\langle a_n\pm b_n\rangle\) 的普通生成函数。
考虑乘法运算,也就是卷积:
因此 \(F(x)G(x)\) 是序列 \(\langle \sum_{i=0}^n a_ib_{n-i} \rangle\) 的普通生成函数。
封闭形式
在运用生成函数的过程中,我们不会一直使用形式幂级数的形式,而会适时地转化为封闭形式以更好地化简。
例如 \(\langle 1,1,1,\cdots\rangle\) 的普通生成函数 \(F(x)=\sum_{n\ge 0}x^n\),我们可以发现
那么解这个方程得到
这就是 \(\sum_{n\ge 0}x^n\) 的封闭形式。
考虑等比数列 \(\langle 1,p,p^2,p^3,p^4,\cdots\rangle\) 的生成函数 \(F(x)=\sum_{n\ge 0}p^nx^n\),有
等比数列的封闭形式与展开形式是常用的变换手段。
小练习
请求出下列数列的普通生成函数(形式幂级数形式和封闭形式)。难度是循序渐进的。
- \(a=\langle 0,1,1,1,1,\cdots\rangle\)。
- \(a=\langle 1,0,1,0,1,\cdots \rangle\)。
- \(a=\langle 1,2,3,4,\cdots \rangle\)。
- \(a_n=\binom{m}{n}\)( \(m\) 是常数,\(n\ge 0\))。
- \(a_n=\binom{m+n}{n}\)( \(m\) 是常数,\(n\ge 0\))。
答案
第一个:
第二个:
第三个(求导):
第四个(二项式定理):
第五个:
可以使用归纳法证明。
首先当 \(m=0\) 时,有 \(F(x)=\frac{1}{1-x}\)。
而当 \(m \geq 0\) 时,有
斐波那契数列的生成函数
接下来我们来推导斐波那契数列的生成函数。
斐波那契数列定义为 \(a_0=0,a_1=1,a_n=a_{n-1}+a_{n-2}\;(n>1)\)。设它的普通生成函数是 \(F(x)\),那么根据它的递推式,我们可以类似地列出关于 \(F(x)\) 的方程:
那么解得
那么接下来的问题是,如何求出它的展开形式?
展开方式一
不妨将 \(x+x^2\) 当作一个整体,那么可以得到
我们得到了 \(a_n\) 的通项公式,但那并不是我们熟知的有关黄金分割比的形式。
展开方式二
考虑求解一个待定系数的方程:
通分得到
待定项系数相等,我们得到
解得
那么我们根据等比数列的展开式,就可以得到斐波那契数列的通项公式:
这也被称为斐波那契数列的另一个封闭形式(\(\frac{x}{1-x-x^2}\) 是一个封闭形式)。
对于任意多项式 \(P(x),Q(x)\),生成函数 \(\frac{P(x)}{Q(x)}\) 的展开式都可以使用上述方法求出。在实际运用的过程中,我们往往先求出 \(Q(x)\) 的根,把分母表示为 \(\prod (1-p_ix)^{d_i}\) 的形式,然后再求分子。
当对分母进行因式分解但有重根时,每有一个重根就要多一个分式,如考虑生成函数
的系数的通项公式,那么有
解得
那么
普通生成函数的组合意义
砝码称重
假设质量为 \(1\) 克、\(2\) 克、\(3\) 克、\(4\) 克的砝码各一枚,能称出哪些重量?每种重量各有几种方案?
- 约定:形式参数 \(x^i\) 表示取了 \(i\) 克的物品。
为了方便陈述,对于某一类砝码我们设辅助序列 \(a\),若重量 \(i\) 克能取到,则 \(a_i = 1\),否则为0。
- 对于 \(1\) 克砝码的下标从 \(0\) 开始。
- 选取 \(1\) 克砝码,不取时为 \(0\) 克,取时为 \(1\) 克,故序列 \(a = \langle 1, 1, 0, 0, 0, \cdots, 0 \rangle\),故该砝码选取的方案的GF(生成函数)为 \(1x^0 + 1x^1 + 0x^2 + \cdots + \cdots + 0x^{\infty}\),因为后面系数全是 \(0\),故其选取方案即表示为 \((1 + x^1)\),GF即表示为 \((1 + x^1)\)
- 同理,\(2\) 克砝码取方案,故序列 \(a = \langle 1, 0, 1, 0, 0, 0, \cdots \rangle\),GF即表示为 \((1 + x^2)\)
- 同理,\(3\) 克砝码取方案,故序列 \(a = \langle 1, 0, 0, 1, 0, 0, \cdots \rangle\),GF即表示为 \((1 + x^3)\)
- 同理,\(4\) 克砝码取方案,即表示为 \((1 + x^4)\)
故总的生成函数为:
\(F(x) = (1 + x^1)(1 + x^2)(1 + x^3)(1 + x^4)\)
\(= 1 + x + x^2 + 2x^3 + 2x^4 + 2x^5 + 2x^6 + 2x^7 + x^8 + x^9 + x^{10}\)
每个 \(x^i\) 前面的系数,即是他们的方案数
- 不取、取 \(1\) 克、取 \(8\) 克、取 \(9\) 克、取 \(10\) 克: \(1\) 种方案
- 取 \(2\) 克、取 \(3\) 克、取 \(4\) 克、取 \(5\) 克、取 \(6\) 克、取 \(7\) 克: \(2\) 种方案
拓展1:若每个砝码的数量改为 \(a_k\) 呢?
- 根据上面的思路,不难拓展,只需要修改每一类砝码所对应的 \(a\) 数组,即可得到其对应的GF
- 若对于四个 \(3\) 克砝码,其GF即为 \((1 + x^3 + x^6 + x^9 + x^{12})\)
拓展2:若我们想知道具体用哪几种砝码该怎么办?
- 原本GF(生成函数)的一项的表示形式为 \(a_i x^i\),表示取 \(i\) 克的砝码有 \(a_i\) 种方案
- 我们将每个砝码的选取的表示,变换为: \(y_j^{k} \times x^i\),表示取 \(i\) 克的砝码,需要使用质量为 \(j\) 克的砝码 \(k\) 个
- 如,对于一个 \(2\) 克砝码来说,就是 \((1 + y_2^1 x^2)\)
- 如,对于两个 \(3\) 克砝码来说,就是 \((1 + y_3^1 x^3 + y_3^2 x^6)\)
最后的GF中 \(x^i\) 前面的系数就会形如 \((y_1^{k_1} y_2^{k_2} y_3^{k_3} + \cdots)\) 的形式,我们也就可以通过其中的一项得出具体方案,如 \(2\) 克的砝码取 \(k_4\) 个即可。
取球问题
假设有 \(n\) 个箱子,所有箱子里的球都相同,第 \(i\) 个箱子里面有 \(b_i\) 个球,求解拿取 \(1 \sim \sum_{i=1}^n b_i\) 个球分别有多少种方案?
- 约定:形式参数 \(x^i\) 表示取了 \(i\) 个球。
我们同样约定辅助序列 \(a\),若能取到 \(i\) 个球,则 \(a_i = 1\),否则为0
- 对于第 \(1\) 个箱子,容易得到:当 \(j \in [0, b_1]\) 时,\(a_j = 1\);当 \(j > b_1\) 时,\(a_j = 0\),故其GF为 \((1 + x + x^2 + \cdots + x^{b_1})\)
- 同理,对于第 \(k\) 个箱子,其GF为 \((1 + x + x^2 + \cdots + x^{b_k})\)。
故总的GF为:
\(F(x) = (1 + x + x^2 + \cdots + x^{b_1})(1 + x + x^2 + \cdots + x^{b_2}) \cdots (1 + x + x^2 + \cdots + x^{b_n})\)
P10780 BZOJ3028 食物
明明这次又要出去旅游了,和上次不同的是,他这次要去宇宙探险!我们暂且不讨论他有多么 NC,他又幻想了他应该带一些什么东西。理所当然的,你当然要帮他计算携带 \(n\) 件物品的方案数。
他这次又准备带一些受欢迎的食物,如:蜜桃多啦,鸡块啦,承德汉堡等等。
当然,他又有一些稀奇古怪的限制:
每种食物的限制如下:
- 承德汉堡:偶数个;
- 可乐: \(0\) 个或 \(1\) 个;
- 鸡腿: \(0\) 个,\(1\) 个或 \(2\) 个;
- 蜜桃多:奇数个;
- 鸡块: \(4\) 的倍数个;
- 包子: \(0\) 个,\(1\) 个,\(2\) 个或 \(3\) 个;
- 土豆片炒肉:不超过一个;
- 面包: \(3\) 的倍数个;
注意,这里我们懒得考虑明明对于带的食物该怎么搭配着吃,也认为每种食物都是以『个』为单位(反正是幻想嘛),只要总数加起来是 \(n\) 就算一种方案。因此,对于给出的 \(n\),你需要计算出方案数,并对 \(10007\) 取模。
- 对于所有数据,\(1\leq n\leq 10^{500}\);
这是一道经典的生成函数题。对于一种食物,我们可以设 \(a_n\) 表示这种食物选 \(n\) 个的方案数,并求出它的生成函数。而两种食物一共选 \(n\) 个的方案数的生成函数,就是它们生成函数的卷积。多种食物选 \(n\) 个的方案数的生成函数也是它们生成函数的卷积。
在理解了方案数可以用卷积表示以后,我们就可以构造生成函数(标号对应题目中食物的标号):
- \(\displaystyle\sum_{n\ge 0}x^{2n}=\frac{1}{1-x^2}\)。
- \(1+x\)。
- \(1+x+x^2=\frac{1-x^3}{1-x}\)。
- \(\frac{x}{1-x^2}\)。
- \(\displaystyle \sum_{n\ge 0}x^{4n}=\frac{1}{1-x^4}\)。
- \(1+x+x^2+x^3=\frac{1-x^4}{1-x}\)。
- \(1+x\)。
- \(\frac{1}{1-x^3}\)。
那么全部乘起来,得到答案的生成函数:
然后我们将这个东西展开,你可以广义二项式定理,当然也可以直接展开。但是直接展开太麻烦了,我们还是广义二项式定理吧:
所以答案就是 \(\frac{1}{6}k(k+1)(k+2)\)。
注意需要边读入边取模。
实现
1 2 3 4 5 6 7 8 9 10 11 12 | |
P4451 [国家集训队] 整数的lqp拆分
lqp在为出题而烦恼,他完全没有头绪,好烦啊…
他首先想到了整数拆分。整数拆分是个很有趣的问题。给你一个正整数 \(N\) ,对于N的一个整数拆分就是满足任意 \(m \geq 0\), \(a_1 ,a_2 ,a_3…a_m \geq 0\),且 \(a_1+a_2+a_3+…+a_m=n\) 的一个有序集合。通过长时间的研究我们发现了计算对于 \(n\) 的整数拆分的总数有一个很简单的递推式,但是因为这个递推式实在太简单了,如果出这样的题目,大家会对比赛毫无兴趣的。
然后 lqp 又想到了斐波那契数。定义 \(F_0=0,F_1=1,F_n=F_{n-1}+F_{n-2} (n>1)\), \(F_n\) 就是斐波那契数的第 \(n\) 项。但是求出第 \(n\) 项斐波那契数似乎也不怎么困难…
lqp 为了增加选手们比赛的欲望,于是绞尽脑汁,想出了一个有趣的整数拆分,我们暂且叫它:整数的lqp拆分。
和一般的整数拆分一样,整数的 lqp 拆分是满足任意 \(m \geq 0\), \(a_1 ,a_2 ,a_3…a_m \geq 0\),且 \(a_1+a_2+a_3+…+a_m=n\) 的一个有序集合。但是整数的lqp拆分要求的不是拆分总数,相对更加困难一些。
对于每个拆分,lqp 定义这个拆分的权值 \(F_{a_1}F_{a_2}…F_{a_m}\),他想知道对于所有的拆分,他们的权值之和是多少?
简单来说,就是求
\(\sum\prod_{i=1}^m F_{a_i}\)
\(m \geq 0\)
\(a_1,a_2...a_m \geq 0\)
\(a_1+a_2+...+a_m=n\)
由于答案可能非常大,所以要对 \(10^9 + 7\) 取模。
【数据范围】 对于 \(100\%\) 的数据,\(1\le n \le 10^{10000}\)。
首先考虑 Fibonacci 数的生成函数
我们知道拆分为恰好 \(k\) 个数时,答案的生成函数为 \(F(x)^k\),那么答案的生成函数显然为
分母写成递推式就是
乘上分子得到答案
解递推式特征方程,得
设通项公式为
分别代入 \(n = 0, n = 1\),解得
注意到 \(\sqrt{2}\) 在模 \(10^9 + 7\) 下存在(\(59713601\)),最终答案就出来了
直接计算即可。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
P4389 付公主的背包
付公主有一个背包
这个背包最多可以装 \(10^5\) 大小的东西
付公主有 \(n\) 种商品,她要准备出摊了
每种商品体积为 \(v_i\),都有无限件
给定 \(m\),对于 \(s\in [1,m]\),请你回答用这些商品恰好装 \(s\) 体积的方案数
对于 \(100\%\) 的数据, \(1\le n,m \le 10^5\), \(1\le v_i \le m\)。
每个物品的个数为无限,那么对于一个体积为 \(V\) 的物品,我们可以直接构建生成函数。
显然答案就是 \(m\) 个生成函数的卷积。
如果直接做答案是 \(O(mn \log n)\),显然不是正确的复杂度。
我们看看怎么优化。生成函数除了形式幂级数的形式还有封闭形式,在 \(A(x)\) 后面已写出封闭形式,所以只需要把封闭形式的卷积求出来,然后求逆还原多项式。但这样复杂度是 \(O(2^m)\),还是不行。
我们考虑把乘法化简,自然想到取对数变成加法。多项式求 \(\ln\) 复杂度是 \(O(n \log n)\),但我们的多项式特殊,有:
推导如下:设 \(\ln F(x) = G(x)\),则
于是,统计每一个容量的物品个数,然后 \(O(m / V)\) 给对应位置加上系数,总复杂度是 \(O(n \log n)\)。做完这步相当于给每个封闭形式 \(\ln\) 后求和。直接多项式 \(\exp\) 再多项式求逆后还原多项式,输出答案即可。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |