【讲稿】8.13杂题选讲 | 【学习笔记】普通生成函数 | 【题解】 luogu P2000 拯救世界

  • UPD on 8.11:纠正了关于斐波那契数列待定系数法的方程
  • UPD on 8.12:纠正了关于收敛半径的描述

又要杂题选讲了qwq,由于本人太菜而且暑假集训后完全在搞whk啥题都没刷,于是只能讲模板了/kk

一.啥是生成函数

生成函数是一个晾衣绳,我们把一系列的数字挂在上面以供显示。——《\(Generating\) \(Funtionology\)

上面那句话是什么意思,我们姑且不谈反正我也不知道是什么意思。我们从中大概可以感受到,生成函数是一种处理序列问题的工具,那么我们接下来再看这本书里的另一端话:

假设我们有一个问题,答案是一系列的数字\(\{ a_i \}\),我们想知道这个序列是什么。我们会得到什么样的答案呢?
一个简单的公式将是最好的。如果我们发现对于每个 \(n=0,1,2,…,a_n=n^2+3\),那么毫无疑问我们已经“回答”了这个问题。
但是如果没有简单的公式来计算未知序列?毕竟,有些序列是复杂的——举个令人毛骨悚然的例子,假设未知序列是\(\{ 2,3,5,7,11,13,17,19,…,\}\) 其中 \(a_n\) 是第 \(n\) 个素数。那么,它指望得到任何一种简单的公式都是不合理的。
生成函数给你提供另外一种解决这种问题的有力方式,虽然给序列成员一个简单的公式是不可能的,但是我们可以给出一个关于幂级数和的简单公式,它的系数就是我们要找的序列。——《\(Generating\) \(Funtionology\)

好的,我们现在已经知道生成函数是用来干什么的了,接下来我们直接切入正题——

二.普通生成函数 \(opsgf\) 的定义

我们定义一个序列 \(\{ a_i \}\) 的普通生成函数为

\[F(x)=\sum\limits_{k}{a_kx^k} \]

\(\{ a_i \}\) 既可以是有穷序列,也可以是无穷序列,下面有一些常见的例子:

  1. 序列 \(\{1,2,3\}\) 的普通生成函数是 \(F(x)=1+2x+3x^2\)
  2. 序列 \(\{1,1,...,1\}\) 的普通生成函数是 \(\sum\limits_{k}{x^k}\)

换句话说,如果序列 \(\{ a_i \}\) 有通项公式,那么他的生成函数的系数就应该是他的通项公式。
知道了上面的东西并没有任何用处,我们不知道怎么应用它,而他的用法——求封闭形式,就要从我们提到过的序列 \(\{1,1,...,1\}\) 的普通生成函数

\[F(x)=\sum\limits_{k}{x^k} \]

讲起——

三.普通生成函数的封闭形式

大家可能在很多地方都见到过这样一个定理:

\[\sum\limits_{k \geq 0}{x^k}=\frac{1}{1-x} \]

这个定理的正确性看起来一点也不显然嘛,那我们换个角度看问题:
有个非常显然的性质:

\[F(x)x+1=F(x) \]

其实他就相当于把 \(F(x)\) 整体向右平移一位,然后在前面补 \(1\) ,其实这种方法类似扰动法的解方程,我们把上面的那个方程解了,就得到了这个定理

\[\sum\limits_{k \geq 0}{x^k}=\frac{1}{1-x} \]

这玩意的收敛半径是 \(|x| < 1\)
如果你理解了上面的过程,那么恭喜你,你已经初步掌握了生成函数的处理技巧之一——求封闭形式。
我们把上面的过程称为求封闭形式,把从 \(\frac{1}{1-x}\) 推回原来的函数称为级数展开,而且显然这两个东西是可以互推的。
好的,那么在我们初步学会了普通生成函数之后,我们来看一个应用。

四.利用普通生成函数解斐波那契数列的通项问题

  • 问题:定义斐波那契数列 \(f_i=f_{i-1}+f_{i-2},f_1=f_2=1\),求 \(\{ f_i \}\) 通项。

解:首先设 \(\{ f_i \}\) 的普通生成函数为

\[F(x)=\sum\limits_{k \geq 1}{f_kx^k} \]

这个套路在《\(Generating\) \(Funtionology\)》里面被称为 \(Snake\) \(Oil\) \(Method\),继续按套路,设

\[G(x)=\sum\limits_{k \geq 3}{f_kx^k}=F(x)-x-x^2 \]

其实这个套路跟我们上面求 \(\sum\limits_{k \geq 0}{x^k}\) 的作用是一样的,都是通过发现 \(F(x)\) 的性质从而解出他的封闭形式,那么有:

\[G(x)=\sum\limits_{k \geq 3}{f_kx^k}=\sum\limits_{k \geq 3}{(f_{k-1}+f_{k-2})x^k}=x\sum\limits_{k \geq 2}{f_kx^k}+x^2 \sum\limits_{k \geq 1}{f_kx^k}=x(F(x)-x)+x^2F(x) \]

结合 \(G(x)=F(x)-x-x^2\),移项解得:

\[F(x)=\frac{x}{1-x-x^2} \]

这就是斐波那契数列的生成函数。
到了这一步,我们将它级数展开,按套路设:

\[\frac{x}{1-x-x^2}=\frac{x}{(1-x·x_1)(1-x·x_2)}=\frac{A}{1-x·x_1}+\frac{B}{1-x·x_2} \]

可解得 \(x_1=\frac{1 + \sqrt 5}{2},x_2=\frac{1 - \sqrt 5}{2}\),整理上式有:

  • \(\frac{x}{1-x·x_2}=A+\frac{1-x·x_1}{1-x·x_2}B\)
  • \(\frac{x}{1-x·x_1}=B+\frac{1-x·x_2}{1-x·x_1}A\)

这个是一个非常经典的 \(trick\),推很多别的生成函数的式子的时候都可以这样子做,在这两个式子中分别令 \(x·x_1=1,x·x_2=1\),最后可解出:

\[A=\frac{1}{\sqrt 5},B=-\frac{1}{\sqrt 5} \]

\(F(x)=\frac{A}{1-x·x_1}+\frac{B}{1-x·x_2}\)级数展开:

\[F(x)=A(\sum\limits_{k \geq 1}{(x·x_1)^k})+B(\sum\limits_{k \geq 1}{(x·x_2)^k}) \]

\(A,B,x_1,x_2\) 全部代入,最后有:

\[F(x)=\sum\limits_{k \geq 1}[\frac{1}{\sqrt 5}(\frac{1 + \sqrt 5}{2})^k-\frac{1}{\sqrt 5}(\frac{1 - \sqrt 5}{2})^k]x^k \]

于是有斐波那契数列的通项公式:

\[f_n=[x^n]F(n)=\frac{1}{\sqrt 5}(\frac{1 + \sqrt 5}{2})^n-\frac{1}{\sqrt 5}(\frac{1 - \sqrt 5}{2})^n \]

好的,看来我们现在已经成功应用普通生成函数解决了一些有趣的问题了,下面我们对上面的思想/套路作一些总结:

  1. 首先,我们要求序列 \(\{ a_i \}\) 的通项公式,那么就把它的普通生成函数 \(F(x)\) 设出来
  2. 求出 \(F(x)\) 的封闭形式
  3. 再把 \(F(x)\) 的封闭形式展开,取系数,就得到了答案

好哇,这就是我们今天杂题选讲的全部内容了,大家再见!
好吧,既然是杂题选讲,还是得讲点题的。

五.例题:LG P2000 拯救世界

我们将题目中的限制所代表的生成函数列出来(具体我们讲题的时候解释):

\[\begin{array}{l} 1+x^{6}+x^{12}+\cdots=\frac{1}{1-x^{6}} \\ 1+x+x^{2}+\cdots+x^{9}=\frac{1-x^{10}}{1-x} \\ 1+x+x^{2}+\cdots+x^{5}=\frac{1-x^{6}}{1-x} \\ 1+x^{4}+x^{8}+\cdots=\frac{1}{1-x^{4}} \\ 1+x+x^{2}+\cdots+x^{7}=\frac{1-x^{8}}{1-x} \\ 1+x^{2}+x^{4}+\cdots=\frac{1}{1-x^{2}} \\ 1+x=\frac{1-x^{2}}{1-x} \\ 1+x^{8}+x^{16}+\cdots=\frac{1}{1-x^{8}} \\ 1+x^{10}+x^{20}+\cdots=\frac{1}{1-x^{10}} \\ 1+x+x^{2}+x^{3}=\frac{1-x^{4}}{1-x} \end{array}\]

然后把他们全部乘起来,得到

\[\frac{1}{(1-x)^{5}} \]

有一个这样子的结论(广义二项式定理):

\[\frac{1}{(1-x)^{n}}=\sum_{i=0}^{\infty} {{n+i-1} \choose {i}} x^{i} \]

那么我们上面得到的式子就是 \(n=5\) 的情况,所以:

\[\frac{1}{(1-x)^{5}}=\sum_{i=0}^{\infty} {{i+4} \choose {i}} x^{i} \]

所以答案就是 \({n+4} \choose {n}\),这题做完了。

凑合着放一下代码

n=int(input())
print((n+1)*(n+2)*(n+3)*(n+4)//24)

你问我为啥写 \(Python\)?
是不可能写高精度的,这辈子都不可能再写高精度的
UPD. \(Py\)被卡了,所以还是老实写高精压位或者 \(ntt\) 优化吧/kk

完结撒花!!!

posted @ 2020-08-09 22:47  L_G_J  阅读(230)  评论(3编辑  收藏  举报