学习笔记——生成函数初见
可能不严谨,但是能看懂,并且可以用~
概论
你从小就被 \(\texttt{m}\color{red}{\texttt{yee}}\) 在生物课上教育生成函数,但是那个时候的你,连生成函数是什么都不知道。
后来,你在学考前粗粗地看了看,发现完全没法看懂。
最后,听 \(\texttt{M}\color{red}{\texttt{r_Spade}}\) 讲生成函数,终于初步弄懂了生成函数,并经过一些例题的磨炼稍微能推一下式子,于是就来这里写博客了。
首先我们需要一些前置芝士——多项式及其基本运算。
前置芝士
主要介绍多项式和基本的运算,如果你已经熟练掌握,可跳过此部分。
多项式表示
这个大家都会,用一个 \(f\) 来表示一个多项式,而 \([x^n]f\) 表示多项式 \(f\) 的第 \(n\) 次项的系数。
多项式乘法
你可以考虑一波小学的时候学的分配律,然后合并同类项,就是我们所说的多项式乘法。基于此,我们有一个 \(O(n^2)\) 的乘法:
我们把这个玩意儿记作:
然后如果你会 FFT 或者 NTT 等算法,可以把它优化到 \(O(n\log n)\)。
顺带一提(下面要用),若干多项式相乘会有如下形式:
多项式求逆
有的时候,我们会在模意义下,求两个多项式的乘法,如果多项式 \(f,g\) 满足:
那么 \(f,g\) 就是互逆的,我们类比于整数的逆元,记作:
那么我们根据乘法的定义,可以推出求逆的式子:
多项式复合
也就是喜闻乐见的函数嵌套,我们记作 \(f\circ g\)。一般不会直接计算。
多项式求导
这东西大家都会,有:
然后一些性质:
根据这些性质,我们可以得到:
多项式 exp
也称为多项式指数函数。记作:\(e^x\)。
其来源于我们希望能得到一个多项式使得其导数是其本身。根据求导的定义,我们有:
此时我们令 \([x^0]f=1\),则有:
那么我们定义一个多项式 \(f\) 的指数函数为其和 \(e^x\) 的复合,记作:\(e^f\)。则易得:
根据上式,我们注意到 \(f^k\) 的前 \(k\) 项是 \(0\),所以如果我们需要求指数函数的第 \(n\) 项系数,可以这样子:
顺带一提,指数函数有着代数相似的良好性质:
为了更快地求指数函数,我们利用导数:令 \(g=e^f\),则有:
于是我们分别取第 \(n\) 项系数:
有了这个式子可以 \(O(n^2)\) 递推,当然也可以优化到 \(O(n\log n)\)。
多项式 ln
你可以把这玩意儿看成是代数的对数。即有:
快乐。
至此,多项式的基本运算,也就是生成函数的基础就介绍完毕。
进入正题
普通生成函数
首先,我们需要明确一件事:生成函数是什么。这里抽象地解释一下就是:对于一个数列 \(a\),多项式 \(f\) 如果满足:
则我们称这个多项式 \(f\) 是数列 \(a\) 的一个普通生成函数(OGF)。是的,生成函数是一个一个一个多项式!
引入
考虑一个小学生题。
你有 \(a\) 件不同的上衣,\(b\) 条不同的裤子,\(c\) 双不同的鞋子。问你有多少种不同的搭配方式?
你是这样算的:上衣有 \(a\) 种选择,裤子是 \(b\) 种,鞋子是 \(c\) 种,那么根据乘法原理应当是 \(a\times b\times c\) 种。
然而,如果我们把每种选择用加法原理展开,应该是这样的:
以上只是感受一下。
然后我们继续问:
如果这 \(a\) 件上衣,\(b\) 条裤子和 \(c\) 双鞋子分别都有特定的价格,那请问,总价格是 \(n\) 的方案数是多少?
这个题好像可以 dp?我们可以考虑设 \(dp_{1/2/3,i}\) 表示考虑了衣服/衣服+裤子/衣服+裤子+鞋子之后总价是 \(i\) 的方案数。然后暴力转移就是 \(O(nm)\) 的了,其中 \(m\) 就是 \(a+b+c\)。这个东西已经很难优化了。
但是我们来一波人类智慧,我们利用生成函数,使得其能通过某种代数运算的方式直接完成一整行的转移。我们掏出三个多项式 \(A,B,C\),分别表示衣服裤子和鞋子。然后 \([x^n]A\) 表示衣服中价格为 \(n\) 的数量,以此类推。
然后我们把这三个多项式乘起来,得到 \(f\),那么有:
现在,我们把这个多项式的 \(x\) 的指数看成是上面 \(dp\) 中的 \(i\),那么我们惊奇地发现,这个东西实际上就是 \(dp\) 的转移。然后我们考虑 \(f\) 是啥。我们假设数列 \(b\),其中 \(b_i\) 表示 \(n=i\) 时的答案。不难发现,\(f\) 就是答案数列 \(b\) 的生成函数。
也就是说,我们将这三个多项式乘起来,然后取第 \(n\) 项系数,就是我们需要求的答案。这个东西,用 FFT/NTT 可以优化到 \(O(n\log n)\)。
经过上面这个例子,我们不难发现,生成函数实际上是借助于 \(x\) 的指数来区分不同的系数,可以把它当成 \(dp\) 的下标(或者说是状态)。因此,我们不会向生成函数中带入任何 \(x\) 的值,它只作为一个标志。
求 OGF
有了对生成函数的初步了解,来看几道题吧。
有无穷多张 \(1,5,10,20,50,100\) 元的纸币,求凑成 \(n\) 元的方案数。
我们来求它的生成函数。考虑每种纸币选多少,那么对于面值是 \(i\) 的纸币,其构成多项式:
那么我们将这些多项式全部乘起来,和第一个例子相同,相当于让他们自由组合。最后这个多项式就是答案序列的生成函数,那我们取第 \(n\) 项系数,就是答案了。
设斐波那契数列生成函数 \(f\),且 \([x^0]f=0,[x^1]f=1\),求 \(f\)。
不同与之前所遇到的组合题,这题直接要求一个特定数列的生成函数。面对这种问题,通常选择先写出递推式子:\([x^n]f=[x^{n-1}]f+[x^{n-2}]f\),\(n\ge 2\)。然后我们把他全部转化成同一项的系数,即:\([x^n]f=[x^n]fx+[x^n]fx^2\),通过移项化简得到:
也就是说多项式 \(f\) 乘上多项式 \(1-x-x^2\) 后第 \(n\) 项(\(n\ge 2\))的系数永远是 \(0\)。
然后我们考虑 \(n=1\) 的情况,那么有:
以及 \(n=0\) 的情况,那么有:
那么也就是说,对于多项式 \(g=f(1-x-x^2)\),只有一次项系数为 \(1\),其余都是 \(0\),那么就容易得到 \(g=x\),那么就有:
这便是斐波那契数列的生成函数。
指数生成函数
我先不说这是个啥,我们先来引入。
引入
先来考虑这样一个问题:
给你一张完全图,求生成树的个数。
这个问题直接用矩阵树定理,求行列式之后可以得到应该是 \(n^{n-2}\)。
现在我们把这个题升级一下:
求 \(n\) 个点的无向图,形状恰好是不连通的两棵树的图的数量。
看到这个题可以想到是相当于求两棵不相交的生成树。那么想要把图分成两部分。于是你写出了这样一个暴力求的式子:
这个 \(\dfrac{1}{2}\) 是因为我们需要去除两棵树的顺序。
类似于上面提到的 OGF,我们尝试把这玩意儿变成答案数列的生成函数的系数,即:
我们巧妙地分配这个组合数的阶乘,得到:
然后我们非常智慧地定义一个 \(g\) 是第一个问题的生成函数,那么有:
欸那我们带入上面这个式子不就是:
然后我们有一个 \([x^n]F=\dfrac{[x^n]f}{n!},[x^n]G=\dfrac{[x^n]g}{n!}\)。带入得到:
很神奇,如果我们知道了 \(G\)(而我们确实容易计算这个多项式),那么通过多项式乘法就能得到 \(F\) 了。复杂度可以是 \(O(n\log n)\)。
我们发现上面这个 \(F,G\) 两个多项式和 OGF 不同,它在 OGF 的基础上除了一个阶乘,很像多项式指数函数的形式,所以我们叫它指数生成函数(EGF)。
有了 EGF,我们可以继续升级上面这个问题:
EGF 的进一步应用
现在来考虑这样一个问题:
求 \(n\) 个点的无向图中,形状恰好是不连通的若干棵树的图的数量。
换句话说,我们把前面的 \(2\) 变成了若干。
同样我们先写出这个暴力的式子:
模仿上面分配这个组合数,得到:
你是否觉得眼熟,啊对,这东西就是指数函数:
好家伙。。。
总结
我们现在遇到了两种生成函数,也是最常见的两种。这两种生成函数都会用于组合计数类问题中。OGF 擅长对于组合成为特定值的问题,而 EGF 擅长对于树图的计数,其特点是暴力计算式子中含有二项式,然后通过巧妙分配其阶乘可以得到 EGF。