生成函数

Published: by Creative Commons Licence

  • Tags:

概率生成函数

对于取值为自然数的随机变量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)izi1|z=1=i=1P(X=i)i=E[X]

可以递推推出对于i2

E[Xi]=(zf(i1)(z))|z=1=f(i1)(z)+zf(i)(z)|z=1=f(i1)(1)+f(i)(1)

比较重要的就是计算方差的公式:

var(X)=E[X2]E[X]2=f(1)+f(1)f(1)2

普通生成函数(ODF)

对于序列a0,a1,,它的生成函数为f(x)=i0aixi,记[f(x)]i表示其xi项的系数ai

  • 两个序列<ai>与<bi>的生成函数的加法运算的结果得到的是<ai+bi>的生成函数。
  • 两个序列<ai>与<bi>的生成函数的乘法运算的结果得到的是<0jiajbij>的生成函数。

一般我们在做生成函数的运算的时候,采用封闭形式能够加快计算的效率。下面是几个比较常用的封闭形式:

  • <1>的生成函数为i0xi=11x
  • <[k|i]>的生成函数为i0xki=11xk
  • <i>的生成函数为i0ixi=x(1x)2
  • <(ni)>的生成函数为i0(ni)xi=(1+x)n
  • <(n+ii)>的生成函数为i0(n+ii)xi=1(1x)n+1
  • <1i!>的生成函数为i01i!xi=ex
  • <1i>的生成函数为i11ixi=ln(1x)=ln(11x)

特别注意由于ex=i=0xii!。因此有i=k(mod2)xii!=ex+(1)kex2,其中k=0,1

题目1:计算a1++an+b1++bm+c1++ck=T的方案数,其中所有变量为非负整数,且a1,,an只能取奇数,b1,,bm只能取偶数,c1,,ck为任意非负整数。问有多少种不同的赋值方案,其中1n,m,k100,将结果模上素数p

我们要计算的是生成函数f(x)=(x(1x2))n(11x2)m(11x)k=xn(1+x)k(1x2)n+m+k的第T项系数。或者是g(x)=(1+x)k(1x2)n+m+k的第Tn按项系数。

进一步展开可以得到:

g(x)=[i0(ki)xi][i0(n+m+k1+ii)x2i]

搞出i0(ki)xi,枚举O(k)i0(n+m+k1+ii)x2i的系数就可以算出来了。时间复杂度为O(klog2p+n+m),还是相当快的。

一道题目

题目2:给定n个非负未知整数a1,,an,要求aiRi,且a1++an=T。问有多少种方案,结果模上素数p,这里Ri106T1071n10p106

这个问题可以用DP解决,时间复杂度为O(nT)

也可以用组合数学+容斥解决,时间复杂度为O(2n+p)

这里讲一下生成函数的做法。可以发现我们要计算的是ni=11xRi+11x=ni=11xRi+1(1x)n。容易发现上面的分子最多只有2n项的系数非0,我们可以用哈希表来表示,计算的时间复杂度为O(n2n)。我们可以将分母展开,得到:

ni=11xRi+1ni=11x=f(x)i0(n1+ii)xi

我们可以遍历左边的项求解结果,总的时间复杂度为O(n2n+p)

指数生成函数(EGF)

对于序列a0,a1,,其指数生成函数为f(x)=i0aii!xi

  • 两个序列<ai>与<bi>的生成函数的加法运算的结果得到的是<ai+bi>的生成函数。
  • 两个序列<ai>与<bi>的生成函数的乘法运算的结果得到的是<0ji(ij)ajbij>的生成函数。

一些常用的指数生成函数和其封闭形式的转换。

  • <1>的指数生成函数为i01i!xi=ex
  • <i!>的指数生成函数为i0xi=11x
  • <(i1)!>的指数生成函数为i01ixi=ln(1x)=ln(11x)

生成函数的系数区间和

给定一个生成函数,我们要计算某个封闭形式为f(x)的生成函数,系数和Ri=L[f(x)]i

这是一个比较经典的技巧,就是我们将f(x)乘上RLi=0xi=1xRL+11x,之后取xR项的系数即可。

生成函数解决three sum问题

给定一个长度为n的非负序列a1,,an,令T(t)表示有多少三元对i<j<k,满足ai+aj+ak=t。要求计算T(0),T(1),,T(m),其中1n,m105

这个有很显然的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=ti,j,k互不相同,记它的生成函数为g。之后可以发现每个三元对i<j<k,都有3!种排列,即在g中被统计3!次,因此我们有T(i)=gi/3!

生成函数解决乘积和问题

问题1:给定一个长度为n的非负序列a1,,an,以及一个素数p。要求计算ni=1ni<j(aiaj(modp)),注意结果不模p。其中1n,p5×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=A2Bk表示的是存在多少下标i,j,满足aiaj=rk(modp)。而结果为2p4i=0Birk。但是这里我们下标是可能出现i=j的,这一部分可以手动减去即可。

时间复杂度为O(plog2p+n)

问题2:给定一个长度为n的非负序列a1,,an,以及一个素数p。要求计算ni=1ni<jaiajp,注意结果不模p。其中1n,p5×105

我们考虑问题1中公式的另外一种转换:

ni=1ni<j(aiaj(modp))=ni=1ni<j(aiajpaiajp)=ni=1ni<jaiajpni=1ni<jaiajp

其中

ni=1ni<jaiajp=1p(ni=1ni<jaiajni=1ni<j(aiaj(modp)))

右边公式的两项,前者是非常好算的,维护一个前缀和即可,后者可以通过问题1的方式求解。

时间复杂度为O(plog2p+n)

参考资料