算法学习笔记:生成函数

生成函数浅讲

感觉这是一个非常牛逼的东西,写了点自己的感悟,可能讲得不是很清楚。

生成函数的定义就比较牛,将数列 {ai} 写成一个函数 A(x)=aixi 的形式叫做普通生成函数。

此处的 xi 没有实际意义,只是一个占位符。对于生成函数来说,绝大数多项式的运算法则都是可以用的。

比如:

A(x)+B(x)=(ai+bi)xi

A(x)B(x)=ij(aibj)xi+j

一些常见的数列都可以写作生成函数的形式,而且往往都有较为简单的封闭形式:

{1,0,0}1

{0,1,0}x

{1,1,1}1+x+x2+=11x

{1,0,1,0,1}1+x2+x4=11x2

我们用 [xn] 表示 xn 的系数,即数列的第 n 位。

拓展一下它们的乘积有什么含义

xk 相当于将数列向后平移 k 位。

11x 相当于对数列做一次前缀和,而 1(1x)k 就是求 k 次前缀和,把这个过程写出来就会发现很像一个旋转了一下的杨辉三角,第 i 就是 (i+k1k1)

11xp 后第 i 位就是原数列第 i 位往前第 p 的整数倍的位的前缀和(听着很绕其实很简单,笔者试图用简洁的语言的描述但语文水平不允许),类似的 1(1xp)k 的数列只在 p 的整数倍的位上有值并且第 ip 位为 (i+k1k1)

然后我们就可以用生成函数做一些很牛的事情。

先看一道简单题:

P10780 食物

题意:在许多不同种类的食物中选出 n 个,每种食物的限制如下:

  • 承德汉堡:偶数个

  • 可乐:0 个或 1

  • 鸡腿:0 个,1 个或 2

  • 蜜桃多:奇数个

  • 鸡块:4 的倍数个

  • 包子:0 个,1 个,2 个或 3

  • 土豆片炒肉:不超过一个。

  • 面包:3 的倍数个

每种食物都是以「个」为单位,只要总数加起来是 n 就算一种方案。对于给出的 n ,你需要计算出方案数,对 10007 取模。(n10500

sol:首先对于每种食物构造一个数列,数列的第 i 位表示总数为 i 时这种食物有多少选法,显然只有 0/1 ,如下:

{1,0,1,0,1}1+x2+x4=11x2

{1,1,0,0,0}1+x

{1,1,1,0,0}1+x+x2

{0,1,0,1,0}x+x3+x5=x1x2

{1,0,0,0,1,0}1+x4+x8=11x4

{1,1,1,1,0}1+x+x2+x3

{1,1,0,0,0}1+x

{1,0,0,1,0}1+x3+x6=11x3

然后把它们乘起来就把数量上的加法转化为了系数上的相加,所以总数为 n 的答案就变成了 xn 的系数。结果化简一下就是 x(1x)4 ,然后结合上面总结的 x(1x)k 的规律,可以得到答案就是 (n+23),边读入边取模即可。

这道题中的生产函数是比较显然的,但是很多题目需要我们通过生产函数转化一些其他做法,并且最终生成函数的通项的求法也会有很多种,下面看一些不同解法的题。

[国家集训队] 整数的lqp拆分 (特征方程)

定义 F0=0,F1=1,Fn=Fn1+Fn2(n>1) (其实就是斐波那契数列)

i=1mFai

m>0

a1,a2...am>0
a1+a2+...+am=n
由于答案可能非常大,所以要对 109+7 取模。(1n1010000

sol :首先考虑DP,设 fi,j 表示固定 minj 时的答案。然后就会发现一个非常显然的暴力转移:

fi,j=k=jfi1,k×Fjk

按理来说这里的 k 只能从 1 枚举到 j1 但是 F0=0,fi,0=0 所以对答案是没有影响的。最终答案就应该是 i=1nfi,n

我们把 fi1fi 的递推看成一次卷积操作,就会发现 fi=Fi ,那么答案就是:

[xn]i=1nFi

看着似乎不太可做,但是我们发现当 i>n 时,Fi 的第 n 项显然为 0 所以可以改写成:

i=1Fi=[xn]F1F

把斐波那契数列的生成函数 F=x1xx2 代进去就是 x12xx2 ,这个生成函数可以用特征方程来解出来,这里详细讲一下。

分子就是平移一位是好处理的,而分母转换一下就是:

f0=1,f1=2,fn+1=2fn+fn1

我们考虑把后面的式子写成一种等比数列的形式:

fn+1pfn=q(fnpfn1)

然后我们可以求出这个等比数列的通项:

fn+1pfn=qn(f1pf0)fn+1pfn=qn(2p)

pq 应该满足条件:

pq=1,p+q=2

所以 pq 是方程 x22x1=0 的两个根 1+212

同时这里也可以看出 pq 是等价的(可以交换),再结合上面的式子可以得到一个方程组:

fn+1qfn=pn(2q)fn+1pfn=qn(2p)

然后我们把 fn 解出来,再把 pq 代进去:

fn=(1+2)n+1(12)n+122

因为这是平移过的,所以最后答案是

fn1=(1+2)n(12)n22

Two Snuke (拉格朗日插值)

题意:对于一个十元组 (a1,a2,b1,b2,c1,c2,d1,d2,e1,e2),定义它合法当且仅当满足下列条件:

  • 0a1<a2

  • 0b1<b2

  • 0c1<c2

  • 0d1<d2

  • 0e1<e2

  • a1+a2+b1+b2+c1+c2+d1,d2+e1+e2n

    现在有 T 组数据,每次对于给定的 n 求所有合法十元组 (a2a1)(b2b1)(c2c1)(d2d1)(e2e1) 之和对 109+7 取模的结果。

    1T100,1n109

这道题似乎有比较简单的组合意义解法,但是我觉得用生成函数做这道题更帅一些,其实是不会组合意义做法。之前写过题解,这里就不讲了。

Card Deck Score (待定系数)

题意:有一些卡牌,每张卡牌上有一个数字,具体的,有 bi 张卡牌上的数字为 ai

求出拿走其中 m 张卡牌的贡献之和。贡献为这些卡牌的乘积。对于本质相同的卡牌组合,只算一次。

  • n16,m1018,bi1017,1ai<mod

sol

首先对于每种卡牌构造生成函数:

j=0biaijxj=1aibi+1xbi+11aix

最终答案的就是:

i=1n1aibi+1xbi+11aix=[xm]i=1n1aibi+1xbi+1i=1n1aix

这里因为 n 很小,只有 16 所以可以2n暴力把分子部分展开。假设展开有 w 项,第 i 项为 cixdi ,则累加每一项的贡献得到的答案就是:

i=1wci×[xndi]1i=1n1aix

现在问题变成了 2n 次询问分母某一位的值。处理分母可以考虑待定系数,如果可以写成求和的形式就好做多了,于是我们令:

i=1n11aix=i=1npi1aixi=1npi1aixj=1n1ajx=1i=1npij=1,jin1ajx=1

一般来说生成函数中的 x 是没有实际意义的,但是这里我们可以考虑把它当作一个普通的函数,而普通函数有一个优势,可以代值求待定系数。所以我们代入 x=1ak 那么当 j=k 时,式子中 1ajx=0 ,也就是说只有 i=k 这一项被保留了下来:

pij=1,jin1ajai=1pi=1j=1,jin1ajai

于是我们可以求出每一项的待定系数,现在再看一下分母的形式就非常好求了:

i=1npi1aix=i=1n[xk]pi1aix=i=1npi×aik

至此,我们得到了一个单次 O(n) 查询某一位的做法,最终复杂度:O(n2n)

posted @   Re_Star  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示