生成函数
概率生成函数
对于取值为自然数的随机变量X,定义概率生成函数为
f(z)=∞∑i=0P(X=i)zi很显然
f(1)=∞∑i=0P(X=i)=1其中P(x=i)为随机变量取到i的概率。利用概率生成函数可以简单地计算期望:
f′(1)=∞∑i=1P(X=i)izi−1|z=1=∞∑i=1P(X=i)i=E[X]可以递推推出对于i≥2有
E[Xi]=(zf(i−1)(z))′|z=1=f(i−1)(z)+zf(i)(z)|z=1=f(i−1)(1)+f(i)(1)比较重要的就是计算方差的公式:
var(X)=E[X2]−E[X]2=f″(1)+f′(1)−f′(1)2普通生成函数(ODF)
对于序列a0,a1,…,它的生成函数为f(x)=∑i≥0aixi,记[f(x)]i表示其xi项的系数ai。
- 两个序列<ai>与<bi>的生成函数的加法运算的结果得到的是<ai+bi>的生成函数。
- 两个序列<ai>与<bi>的生成函数的乘法运算的结果得到的是<∑0≤j≤iajbi−j>的生成函数。
一般我们在做生成函数的运算的时候,采用封闭形式能够加快计算的效率。下面是几个比较常用的封闭形式:
- <1>的生成函数为∑i≥0xi=11−x
- <[k|i]>的生成函数为∑i≥0xki=11−xk
- <i>的生成函数为∑i≥0ixi=x(1−x)2
- <(ni)>的生成函数为∑i≥0(ni)xi=(1+x)n
- <(n+ii)>的生成函数为∑i≥0(n+ii)xi=1(1−x)n+1
- <1i!>的生成函数为∑i≥01i!xi=ex
- <1i>的生成函数为∑i≥11ixi=−ln(1−x)=ln(11−x)
特别注意由于ex=∑i=0xii!。因此有∑i=k(mod2)xii!=ex+(−1)ke−x2,其中k=0,1。
题目1:计算a1+…+an+b1+…+bm+c1+…+ck=T的方案数,其中所有变量为非负整数,且a1,…,an只能取奇数,b1,…,bm只能取偶数,c1,…,ck为任意非负整数。问有多少种不同的赋值方案,其中1≤n,m,k≤100,将结果模上素数p。
我们要计算的是生成函数f(x)=(x(1−x2))n(11−x2)m(11−x)k=xn(1+x)k(1−x2)n+m+k的第T项系数。或者是g(x)=(1+x)k(1−x2)n+m+k的第T−n按项系数。
进一步展开可以得到:
g(x)=[∑i≥0(ki)xi]⋅[∑i≥0(n+m+k−1+ii)x2i]搞出∑i≥0(ki)xi,枚举O(k)个∑i≥0(n+m+k−1+ii)x2i的系数就可以算出来了。时间复杂度为O(klog2p+n+m),还是相当快的。
一道题目。
题目2:给定n个非负未知整数a1,…,an,要求ai≤Ri,且a1+…+an=T。问有多少种方案,结果模上素数p,这里Ri≤106,T≤107,1≤n≤10,p≤106。
这个问题可以用DP解决,时间复杂度为O(nT)。
也可以用组合数学+容斥解决,时间复杂度为O(2n+p)。
这里讲一下生成函数的做法。可以发现我们要计算的是∏ni=11−xRi+11−x=∏ni=11−xRi+1(1−x)n。容易发现上面的分子最多只有2n项的系数非0,我们可以用哈希表来表示,计算的时间复杂度为O(n2n)。我们可以将分母展开,得到:
∏ni=11−xRi+1∏ni=11−x=f(x)∑i≥0(n−1+ii)xi我们可以遍历左边的项求解结果,总的时间复杂度为O(n2n+p)。
指数生成函数(EGF)
对于序列a0,a1,…,其指数生成函数为f(x)=∑i≥0aii!xi。
- 两个序列<ai>与<bi>的生成函数的加法运算的结果得到的是<ai+bi>的生成函数。
- 两个序列<ai>与<bi>的生成函数的乘法运算的结果得到的是<∑0≤j≤i(ij)ajbi−j>的生成函数。
一些常用的指数生成函数和其封闭形式的转换。
- <1>的指数生成函数为∑i≥01i!xi=ex
- <i!>的指数生成函数为∑i≥0xi=11−x
- <(i−1)!>的指数生成函数为∑i≥01ixi=−ln(1−x)=ln(11−x)
生成函数的系数区间和
给定一个生成函数,我们要计算某个封闭形式为f(x)的生成函数,系数和∑Ri=L[f(x)]i。
这是一个比较经典的技巧,就是我们将f(x)乘上∑R−Li=0xi=1−xR−L+11−x,之后取xR项的系数即可。
生成函数解决three sum问题
给定一个长度为n的非负序列a1,…,an,令T(t)表示有多少三元对i<j<k,满足ai+aj+ak=t。要求计算T(0),T(1),…,T(m),其中1≤n,m≤105。
这个有很显然的O(n2)暴力做法。但是这里会超时。
我们可以记c(i)表示i这个数在序列中的出现次数。记f(x)=∑i=0c(i)xi。考虑f3(x)的含义,f3t表示的是所有的三元对i,j,k的数目,满足ai+aj+ak=t。和我们的结果非常相近了,但是存在重复统计。我们可以通过容斥统计出所有的三元对i,j,k的数目,满足ai+aj+ak=t且i,j,k互不相同,记它的生成函数为g。之后可以发现每个三元对i<j<k,都有3!种排列,即在g中被统计3!次,因此我们有T(i)=gi/3!。
生成函数解决乘积和问题
问题1:给定一个长度为n的非负序列a1,…,an,以及一个素数p。要求计算∑ni=1∑ni<j(aiaj(modp)),注意结果不模p。其中1≤n,p≤5×105
一道原题。
我们不好直接计算,可以发现问题很类似于卷积B=A2,但是卷积的结果为Bk=∑i+j=kAiAj。假如我们记Ai表示i这个数在a序列中出现的次数,那么B的结果非常接近我们要的结果,当然我们要的实际上是Ck=∑ij=k(modp)AiAj。
需求不满足,我们可以用数学的方式让它满足。由于从a中删除所有0是不会影响结果的,因此我们可以认为a中没有0。由于乘法在对数的情况下对应的是加法,即logab=loga+logb。即我们可以找到p的任意一个原根r,之后令Ai表示ri在序列a中的出现次数。于是乎在这种意义下B=A2,Bk表示的是存在多少下标i,j,满足aiaj=rk(modp)。而结果为∑2p−4i=0Bi⋅rk。但是这里我们下标是可能出现i=j的,这一部分可以手动减去即可。
时间复杂度为O(plog2p+n)。
问题2:给定一个长度为n的非负序列a1,…,an,以及一个素数p。要求计算∑ni=1∑ni<j⌊aiajp⌋,注意结果不模p。其中1≤n,p≤5×105
我们考虑问题1中公式的另外一种转换:
n∑i=1n∑i<j(aiaj(modp))=n∑i=1n∑i<j(aiaj−p⌊aiajp⌋)=n∑i=1n∑i<jaiaj−pn∑i=1n∑i<j⌊aiajp⌋其中
n∑i=1n∑i<j⌊aiajp⌋=1p(n∑i=1n∑i<jaiaj−n∑i=1n∑i<j(aiaj(modp)))右边公式的两项,前者是非常好算的,维护一个前缀和即可,后者可以通过问题1的方式求解。
时间复杂度为O(plog2p+n)。