Re:从零开始的生成函数魔法

  • 更好的阅读体验

    2021/2/7:初稿

    2021/3/14 Upd:修正了对笛卡尔积的错误理解

    2021/4/16 Upd:更新 【分治+NTT】 / 【数列k次幂和】 / 【点值与下降幂互化】等技术,更新【Exp 的组合意义】

    2021/5/1 Upd:更新 【概率型生成函数】 / 【更多生成函数】

    2021/7/15 Upd:更新【组合对象符号化】,将另一篇博文【浅谈积和类问题】加入,另作了一些修改。

    2021/9/20 Upd:将另一篇博文【浅谈划分类问题】加入。

    2021/12/28 Upd:更新 【分式域与拉格朗日反演】。

    2022/6/6 Upd:更新【拓展-生成函数常见技巧】。

    2022/11/20 Upd:更新【q-模拟】的内容。

广告


本博客较长,可能导致浏览器卡顿(尤其是博客园),如果出现无法滚动等情况请稍等……

由于本人太弱,本文可能层次较浅,没有太深的了解与证明。

其实本文最开始是从学长讲的课学习,后来添加了我了解到与《混凝土数学》里的杂七杂八的一些技术。

本文涉及多项式等知识,问题的模数均为 \(998244353\)

本文主要介绍问题的分析与处理,具体实现可能要用到多项式相关知识。

0. 前置知识

0.1 多项式技术

知道多项式是啥:

\[A(x)=\sum\limits_{i=1}^n a_ix^i \]

您最好还要知道卷积(多项式乘法)是啥不用会写

不,您还要知道多项式求逆,多项式 ln,多项式 exp,牛顿迭代……

广告!浅谈多项式全家桶与实现

  • 分治 + NTT

比较厉害的一个技巧。

  • 给定 \(n\) 个一次多项式 \(a_i+b_ix\),求 \(\prod\limits_{i=1}^n (a_i+b_ix)\)

  • \(n \leq 10^5\)


暴力卷积,是 \(>O(n^2)\) 的……

原因就在于卷着卷着次数会不断变大。

想象一下,我们平时做多个数的乘法,没有人会去顺序一个一个地乘吧……

那可以用类似分治的办法,对于每个区间将左右合并起来。

时间复杂度是 \(T(n)=2T(n/2)+O(n\log n)=O(n\log^2n)\)

具体的实现可以用 vector 存多项式,空间复杂度是 \(O(n\log n)\) 的。


类似的,还可以拓展出分式求和:

  • 给定 \(n\) 个一次多项式 \(a_i+b_ix\),求 \(\sum\limits_{i=1}^n \dfrac{1}{a_i+b_ix}\)

如果直接求逆是 \(O(n^2\log n)\) 的。

同上,对于每个区间维护分子和分母,最后再求逆算答案。

时间复杂度 \(O(n\log^2n)\),空间复杂度 \(O(n\log n)\)

0.2 Taylor 展开

\[f(x)=\sum\limits_{k = 0}^{+\infty} \dfrac{f^{(k)}(0)}{k!}x^k \]

其实这个不是重点。

欲知详情,请看: 知乎-怎样更好地理解并记忆泰勒展开式?

然后有个背下来经典的东西:

\[\ln(\dfrac{1}{1-x})=\sum\limits_{i \geq 1} \dfrac{x^{i}}{i} \]

\[\ln(1-x)=-\sum\limits_{i \geq 1} \dfrac{x^{i}}{i} \]

可以直接泰勒展开,不过也可用别的方法推:

\[\begin{aligned}\ln(1-x) & =\int \dfrac{(1-x)'}{1-x} dx\\& =\int \dfrac{-1}{1-x} dx\\ & =- \int \sum\limits_{i \geq 0}x^{i} dx\\& =- \sum\limits_{i \geq 0} \dfrac{x^{i+1}}{i+1} \\ & =- \sum\limits_{i \geq 0} \dfrac{x^{i+1}}{i+1} \\& =- \sum\limits_{i \geq 0} \dfrac{x^{i+1}}{i+1} \\ & =- \sum\limits_{i \geq 1} \dfrac{x^i}{i} \\\end{aligned} \]


0.3 广义二项式定理

我们都知道喜闻乐见的二项式定理:

\[(1+x)^n=\sum\limits_{i=0}^n \dbinom{n}{i} x^i \]

但有时候,我们想让这个 \(n\) 变成负数甚至小数咋办?

\(f(x)=(1+x)^\alpha\)

我们有 \(f(x)=\sum\limits_{k \geq 0} \dfrac{f^{(k)}(0)}{k!}x^k\)

解释一下求导:

\[f(x)=(1+x)^\alpha \]

\[f^{(1)}(x)=\alpha(1+x)^{\alpha-1} \]

\[f^{(2)}(x)=\alpha(\alpha-1)(1+x)^{\alpha-2} \]

\[\cdots \]

\[f^{(k)}(x)=\alpha^{\underline{k}} (1+x)^{\alpha-k} \]

代入:

\[f(x)=(1+x)^\alpha = \sum\limits_{k \geq 0} \dfrac{\alpha^{\underline{k}} }{k!}x^k \]

啊哈,于是我们还能定义拓展的组合数 \(\dbinom{n}{m}=\dfrac{n^{\underline{m}}}{m!}。\)

1. 普通型生成函数(OGF)

生成函数是啥呢?

我们现在有一个序列 \(\{f_i\}\)

把序列扔到多项式的系数上,就能得到一个对应的多项式:

\[F(x)=\sum\limits_{i=0}^n F_ix^i \]

  • 注意:\(x\) 只是形式的自变量,并没有实际意义,想让它取啥就取啥。

当然,更严谨一点,\(x\) 的取值必须要使 \(F(x)\) 收敛,不过暂不考虑。

下面来介绍一些变换。


1.1 k 阶前缀和

P5488 差分与前缀和

我们有一个数列 \(\{a_i\}\) 和它的生成函数 \(A(x)\),求出它的前缀和 \(\{b_i\}\)

\(\{b_i\}\) 的生成函数是啥呢?

\[b_k=\sum\limits_{i=0}^k a_i=\sum\limits_{i=0}^k a_i \times 1 \]

\[B(x)=(1+x+x^2+x^3+\cdots)A(x) \]

那又多了一个新的多项式 \(f(x)=1+x+x^2+x^3+\cdots\)

事实上,当 \(-1 \leq x \leq 1\) 时,\(f(x)=\dfrac{1-x^{+\infty}}{1-x} \rightarrow \dfrac{1}{1-x}\)

啊?这不是多项式吗?咋变成一个数了?

其实这叫生成函数的封闭形式

哪咋变回来呢?

没事,看广义二项式定理!

\[f(x)=(1-x)^{-1}=\sum\limits_{i \geq 0} \dbinom{-1}{i} (-x)^i \]


当然,要求出 \(k\) 阶前缀和也很简单了:

\[f(x)=(1-x)^{-k}=\sum\limits_{i \geq 0} \dbinom{-k}{i} (-x)^i \]

\[\dbinom{-k}{i}=\dfrac{(-k)(-k-1)\cdots(-k-i+1)}{i!}=(-1)^i\dfrac{k(k+1)\cdots(k+i-1)}{i!}=(-1)^i C_{k+i-1}^i \]

\[f(x)=\sum\limits_{i \geq 0} \dbinom{k+i-1}{i}x^i \]

\(f\)\(A\) 卷一下就行了,\(O(n \log n)。\)

1.2 平移、伸缩与单位根反演

  • 平移

向右平移:直接乘个 \(x^k\) 就好了。

\[A(x)=a_0+a_1x+a_2x^2+a_3x^3+\cdots \]

\[x^kA(x)=a_0x^k+a_1x^{k+1}+a_2x^{k+2}+a_3x^{k+3}+\cdots \]

向左平移:乘个 \(x^{-k}\),但还要先把多余的项减掉。

\[x^{-k}[A(x)-a_0-a_1x- \cdots -a_{k-1}x^{k-1}]=a_k+a_{k+1}x+a_{k+2}x^2+a_{k+3}x^3+\cdots \]

套上求导,就会出现神奇的事情:

\[\vartheta A(x)=xA'(x)=a_1x+2a_2x^2+3a_3x^3+\cdots \]


  • 伸缩

\[A(x^2)=a_0+a_1x^2+a_2x^4+a_3x^6+\cdots \]

\[A(x^k)=a_0+a_1x^k+a_2x^{2k}+a_3x^{3k}+\cdots \]

多么显然!

就好像是把系数往后伸缩了一样。


  • 单位根反演

\[A(x)=a_0+a_1x+a_2x^2+a_3x^3+\cdots \]

\[\dfrac{A(x)+A(-x)}{2}=a_0+a_2x^2+a_4x^4+a_6x^6+\cdots \]

\[?=a_0+a_kx^k+a_{2k}x^{2k}+a_{3k}x^{3k}+\cdots \]

第二个能用 \(A(-x)\) 是因为 \((-1)^2=1\)

那什么 \(x\) 满足 \(x^k=1\) 呢?

聪明的同学们一定都想到了单位根。

\[[k|n]=\dfrac{1}{k} \sum\limits_{i=0}^{k-1}\omega_k^{ni} \]

这玩意叫单位根反演,拿等比数列随便证一下就行了:

\[\sum\limits_{i \geq 0} [k|i] a_ix^i =\sum\limits_{i \geq 0} \dfrac{1}{k} a_ix^i \sum\limits_{j=0}^{k-1}\omega_j^{ij} =\dfrac{1}{k} \sum\limits_{j=0}^{k-1} \sum\limits_{i \geq 0} a_ix^i \omega_j^{ij} =\dfrac{1}{k} \sum\limits_{j=0}^{k-1} A(\omega_k^j x)\]

1.3 生成函数与数列通项(上)

扔一个简单的递推数列,比如斐波那契数列(\(f_n=f_{n-1}+f_{n-2}(n>1)\)\(f_0=0\)\(f_1=1\) ) ,想求它的通项公式

相信大家都知道矩阵乘法。(然而要讲的不是这个

相信大家都知道特征方程。(然而不详细解释

相信大家都知道,生成函数也可以拿来推这个?


\(F(x)\)\(\{f_n\}\) 的生成函数,显然有:

\[F(x)=xF(x)+x^2F(x)+x \]

原因也很简单:

\[f_nx^n=x \cdot f_{n-1}x^{n-1}+x^2 \cdot f_{n-2}x^{n-2},n>1 \]

\(n=1\) 的时候还要再补个 \(x\) ,是边界条件。

然后把 \(F(x)\) 提到左边:

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

这个怎么求呢?我们一会儿再说……

先来看一道更加奇妙的问题……


P4451 [国家集训队]整数的lqp拆分

数列 \(\{f_n\}\) 满足 \(f_n=f_{n-1}+f_{n-2}(n>1)\)\(f_0=0\)\(f_1=1\)

对于 \(n\) 的整数拆分,即满足 \(a_1+a_2+\cdots+a_m=n\)\(\forall \in[1,m],a_i>0\) 的数组 \(a_n\) ,定义其权值为 \(S(a)=f_{a_1}+f_{a_2}+\cdots+f_{a_m}\)

  • \(n\) 的所有整数拆分的权值之和对 \(10^9+7\) 取模的值。

  • \(n \leq 10^{10000}\)


首先解释一下这个奇怪的整数拆分:

如果设选一个数的生成函数为 \(F(x)=f_0+f_1x+f_2x^2+\cdots\)

则整数拆分的生成函数为 \(G(x)=1+F(x)+F(x)^2+\cdots\)

其中 \(F(x)^k\) 就是拆成 \(k\) 个数的方案数。

(其实这是第二章的内容)

那当然把它们变成封闭形式:

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

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

下面来说说怎么把这个奇奇怪怪的封闭形式还原成多项式。


先把它变得好看一点:

\[G(x)=\dfrac{1-x-x^2}{1-2x-x^2}=1+\dfrac{x}{1-2x-x^2} \]

有二次,显然是没办法直接用拓展二项式做的。

不过联想一下初二的数学知识,我们可以把分式裂开成两个一次的

经过一番轻松的解方程能得到:

\[1-2x-x^2=[x-(-1-\sqrt2)][x-(-1+\sqrt2)] \]

这个形式不太好看,给它乘个常数再变一变:

\[[1-(1-\sqrt2)x][1-(1+\sqrt2)x] \]

现在拿它当分母,再用初二的数学知识待定系数:

\[\dfrac{x}{1-2x-x^2}=\dfrac{A}{1-(1-\sqrt2)x}+\dfrac{B}{1-(1+\sqrt2)x} \]

解一解:

\[\dfrac{x}{1-2x-x^2}=\dfrac{-\dfrac{\sqrt2}{4}}{1-(1-\sqrt2)x}+\dfrac{\dfrac{\sqrt2}{4}}{1-(1+\sqrt2)x}=\dfrac{\sqrt2}{4}[-\dfrac{1}{1-(1-\sqrt2)x}+\dfrac{1}{1-(1+\sqrt2)x}] \]

这样就能算了:

\[(1-kx)^{-1}=\sum\limits_i (-1)^i C_{-1}^i k^i x^i=\sum\limits_i k^ix^i \]

是不是很神奇?

当然斐波那契数列也是同样的……

1.4 生成函数与数列通项(下)

  • 本节参考《具体数学》7.3 解递归式

可能在 OI 中没啥用?

看看,就为了解个 \(G(x)=\dfrac{1-x-x^2}{1-2x-x^2}\) 这么小的东西,我们都花了这么大篇幅……

有没有什么规律或者公式可以直接算呢?

那就不得不提到那个喜闻乐见的特征方程,简单介绍一下:

如果有 \(k\) 阶常系数线性递推数列 \(f_n\) 满足 :

\[f_n=a_1f_{n-1}+a_2f_{n-2}+\cdots+a_kf_{n-k} \]

我们可以写一个很好看的方程,即特征方程:

\[x^k=a_1x^{k-1}+a_2x^{k-2}+\cdots+a_k \]

由代数基本定理,该方程在复数域内有且仅有 \(k\) 个根 \(x_1,x_2,\cdots,x_k\)

如果 \(x_i\) 互不相同,则 \(f_n\) 的通项公式形式为 \(f_n=Ax_1^n+Bx_2^n+\cdots。\)

如果有一些相同的根,例如有 \(x_1=x_2=x_3\),那么就把这三项替换成 \((A+Bn+Cn^2)x_1^n\)

\(A,B,C\) 都是待定的系数,需要将点值代入解出)


啊,这个方法看起来挺强的,但是要待定系数……

而且有些时候也许我们只能得到数列的封闭形式。

让我们试着从生成函数的角度去看。

假设已经得到了数列的封闭形式是 \(R(x)=\dfrac{P(x)}{Q(x)}+T(x)\)

其中 \(Q(x)\)\(k\) 次的,\(degP(x)<k\)

延袭刚才的方法,考虑将分式变成一堆形如 \(\dfrac{a}{(1-px)^{m+1}}\) 的和(可能超过 \(k\)!)。

那是因为有:

\[(1-px)^{-m-1}=\sum\limits_{i=0}^{+\infty} \dbinom{-m-1}{i} (-px)^i=\sum\limits_{i=0}^{+\infty} \dbinom{m+i}{i} p^i x^i \]

\[[x^n]\dfrac{a}{(1-px)^{m+1}}=a\dbinom{m+n}{n} p^n \]

真好看……

同时我们发现 \(m=0\) 的时候就是 \(\sum a_ip_i^n\)

\(m>0\) 的时候各个次数的分母都会出来(例如有 \(\dfrac{1}{(1-x)^2}\) 就会有\(\dfrac{1}{1-x}\)),所以也确实要乘上个多项式。

跟特征方程的结果是一样的。

现在的问题就是求 \(a_i\)\(p_i\)

先来考虑 \(p_i\),这相当于将 \(Q(x)\) 表示为 \(q_0\prod\limits_{i=1}^k (1-p_ix)\)\(q_0\)\([x^0]Q(x)\),别晕了)。

我们来考虑在多项式除法里用到的一个技术:将系数对称(”反射“多项式)。

\[Q_R(x)=\sum\limits_{i=0}^k [x^{k-i}]Q(x) x^i=x^k Q(\dfrac{1}{x}) \]

惊奇地发现:

\[Q_R(x)=x^k q_0 \prod (1- p_i \dfrac{1}{x}) = q_0 \prod (x-p_i) \]

所以只需要对 \(Q_r(x)\) 因式分解就能得到 \(p_i\) 了。

那么接下来就是 \(a_i\)

我直接扔两个《具体数学》上的定理。


不同根的有理展开定理

如果 \(R(x)=\dfrac{P(x)}{Q(x)}\),其中 \(Q(x)=q_0 \prod\limits_{i=1}^k (1-p_ix)\),且 \(q_i\) 互不相同,那么:

\([x^n]R(x)=\sum\limits_{i=1}^k a_ip_i^n\),且 \(a_k=\dfrac{-p_kP(\frac{1}{p_k})}{Q'(\frac{1}{p_k})}\)

有理生成函数的一般展开定理

如果 \(R(x)=\dfrac{P(x)}{Q(x)}\),其中 \(Q(x)=q_0 \prod\limits_{i=1}^l (1-p_ix)^{d_i}\),其中 \(\sum d_i=k\)\(q_i\) 互不相同,那么:$$[xn]R(x)=\sum\limits_{i=1}k f_i(n)p_i^n$$,且 \(f_k(x)\) 是次数为 \(d_k-1\) 的多项式,其首项系数为:

\(a_k=\dfrac{(-p_k)^{d_k}P(\frac{1}{p_k})}{Q^{(d_k)}(\frac{1}{p_k})}=\dfrac{P(\frac{1}{p_k})}{(d_k-1)!q_0 \prod\limits_{j \neq k} (1-\dfrac{p_j}{p_k})^{d_j}}\)

好,看来这个已经超过我的理解范围了……

而且这个好像也只能算首项系数……


最后,让我们尝试用这个新方法解决 1.3 中的问题:

\[G(x)=\dfrac{1-x-x^2}{1-2x-x^2}=1+\dfrac{x}{1-2x-x^2} \]

\[Q_R(x)=-1-2x+x^2=[x-(1+\sqrt2)][(x-(1-\sqrt2)] \]

\[Q(x)=1-2x-x^2=[1-(1+\sqrt2)x][(1-(1-\sqrt2)x] \]

求出了 \(p_1=1+\sqrt2\)\(p_2=1-\sqrt2\),接下来先对 \(Q(x)\) 求个导:

\[Q'(x)=-2-2x \]

好,套公式!

\[a_1=\dfrac{-p_1P(\frac{1}{p_1})}{Q'(\frac{1}{p_1})}=\dfrac{-(1+\sqrt2)(\sqrt2-1)}{-2-2(\sqrt2-1)}=\dfrac{\sqrt2}{4} \]

\[a_2=\dfrac{-p_2P(\frac{1}{p_2})}{Q'(\frac{1}{p_2})}=\dfrac{-(1-\sqrt2)(-1-\sqrt2)}{-2-2(-1-\sqrt2)}=-\dfrac{\sqrt2}{4} \]

然后一下子就得到了 1.3 中的答案:

\[G(x)=1+\dfrac{\sqrt2}{4}[\dfrac{1}{1-(1+\sqrt2)x}-\dfrac{1}{1-(1-\sqrt2)x}] \]

1.5 应用-第一类切比雪夫不等式

啊?不就是求常系数递推数列的通项吗?生成函数就这点用?一个常系数线性齐次递推直接爆踩。

让我们来看点更好玩的东西……

UVA11170 Cos(NA) / Trig Function / U160821 倍角

  • \(n\) 次多项式 \(F(x)\) 满足 \(F(\cos x)=\cos(nx)\),求 \(F(x) \bmod{x^{m+1}}\)

  • \(n \leq 10^{10^5}\)\(m < 10^5\)


为了方便,将多项式记作 \(F_n(t)\)

首先我们要相信 \(F(t)\) 是个 \(n\) 次多项式。

为什么呢?

\[\cos(nx+x)+\cos(nx-x)=\cos(nx)\cos x-\sin(nx)\sin x+\cos(nx)\cos x+\sin(nx)\sin x=2\cos(nx)\cos x \]

啊,好像和数列递推差不多

把每个 \(F_n(t)\) 看成是数列 \(\{g_i\}\) 的第 \(n\) 项,不过这个数列的每一项是个多项式

然后还是一样写成封闭形式:

\[g_{n+1}+g_{n-1}=2tg_n \]

\[g_n=2tg_{n-1}-g_{n-2} \]

\[G(x)=2txG(x)-x^2G(x)-tx+1 \]

\[G(x)=\dfrac{1-tx}{1-2tx+x^2} \]

解一下方程,\(x_1=t-\sqrt{t^2-1}\)\(x_2=t+\sqrt{t^2-1}\)

\(1-2tx+x^2=(1-x_1x)(1-x_2x)\)

这里还是用待定系数法来解……

解出来 \(G(x)=\dfrac{1}{2}(\dfrac{1}{1-x_1x}+\dfrac{1}{1-x_2x})\)

所以答案就是:

\[F_n(t)=[x^n]G(x)=\dfrac{1}{2}[(t-\sqrt{t^2-1})^n+(t+\sqrt{t^2-1})^n] \]

时间复杂度 \(O(m\log m)。\)

好像还有进一步的通项公式,这里不展开了。

生成函数是不是很有用呀?

1.5 数列 k 次幂和

P4705 玩游戏

  • 给定 \(n\) 个数 \(a_i\),对 \(\forall k \in [1,m]\)\(S_k=\sum\limits_{i=1}^n a_i^k \bmod 998244353\)

  • \(n,k \leq 10^5\)\(a_i < mod\)


写出答案的生成函数:

\[F(x)=\sum\limits_i x^i \sum\limits_{j=1}^n a_j^i= \sum\limits_{j=1}^n \sum\limits_i a_j^i x^i = \sum\limits_{j=1}^n \dfrac{1}{1-a_jx} \]

后面看起来非常难处理……

其实用分治 + NTT 可以直接爆算!复杂度是 \(O(n\log^2n)\)

看到这个分式,有没有想到可以对 ln 求导?

这是个好主意,因为求导可以相加减,非常好算!

\[\ln(1-ax)'=\dfrac{1}{1-ax} (1-ax)'= \dfrac{-a}{1-ax} \]

啊,上面多了一个 \(a\) ……写一个新的试试:

\[G(x)= \sum\limits_{j=1}^n \ln(1-a_jx)' =- \sum\limits_{j=1}^n \dfrac{a_j}{1-a_jx} = - \sum\limits_{j=1}^n \sum\limits_i a_j^{i+1}x^i = - \sum\limits_i x^i \sum\limits_{j=1}^n a_j^{i+1} \]

于是就有:

\[F(x)=-xG(x)+n \]

\(+n\) 是因为常数项没掉了,要补上。

那现在只要求出 \(G(x)\) 就好了:

\[G(x)= \sum\limits_{j=1}^n \ln(1-a_jx)'= [\sum\limits_{j=1}^n \ln(1-a_jx)]'=[\ln \prod\limits_{j=1}^n (1-a_jx)]' \]

后面这个东西也没有什么好算的办法……还是得用分治 + NTT, 时间复杂度 \(O(n\log^2n)\)

推了个寂寞……

2. 应用-积和类问题

本节主要介绍利用多项式思想与生成函数解决序列上的求和问题。

2.1 集合积和

  • 给出 \(n\) 个变量 \(a_1,a_2,\cdots,a_n\) 的取值,对所有 \(k \in [0,m]\) 求所有它们能组成的 \(k\) 次单项式的和。
  • \(n,m \leq 10^5\)

注:为了方便计算时间复杂度,下文中假设 \(n,m\) 同阶。

似乎无法因式分解,考虑用生成函数,直接考虑每个变量的次数:

\[Ans_k=[x^k]\prod\limits_{i=1}^n(1+a_ix+a_i^2x^2+\cdots)=[x^k]\prod\limits_{i=1}^n \dfrac{1}{1-a_ix} \]

使用分治 + NTT,可在 \(O(n \log^2n)\) 内算出。

发现答案是一堆多项式之积的形式,这是一个很好的形式。

事实上 Matrix-Tree 定理也是这个形式,因此下面许多变式的技巧都可以套到 Matrix-Tree 定理上

2.2 无序背包

随便思考一下,其实就是 \(n\) 种体积为 \(1\) 的物品,第 \(i\) 种物品有 \(a_i\) 种方案(同种物品不同件的方案是区分的),正好填满大小为 \(k\) 的背包的方案数。

拓展 1:每种物品有体积

设第 \(i\) 种物品的体积为 \(V_i\),则:

\[Ans_k=[x^k]\prod\limits_{i=1}^n \dfrac{1}{1-a_ix^{V_i}} \]

其实就是 P4389 付公主的背包

实现方式:取 ln 后用 \(\ln(1-x)\) 的泰勒展开,再分治 + FFT,\(O(n \log^2n)\)

拓展 2:每种物品数量有限制

设第 \(i\) 种物品的体积为 \(t_i\) 件,则:

\[Ans_k=[x^k]\prod\limits_{i=1}^n(1+a_ix+a_i^2x^2+\cdots+a_i^{t_i}x^{t_i})=[x^k]\prod\limits_{i=1}^n \dfrac{1-a_i^{t_i+1}x^{t_i+1}}{1-a_ix} \]

实现方式:分子分母可以分别处理,分子还是取 \(\ln\)\(O(n \log^2n)\)

其实好像是没啥用的……但是有个特例:

2.3 01 背包

\(t_i=1\) 时,\(Ans_k=[x^k]\prod\limits_{i=1}^n (1+a_ix)\)

它代表的意义就是单项式的每个变量的次数不超过 \(1。\)

什么意思?就是 \(a_i\) 中任选 \(k\) 个(不能重复)乘起来再求和。形式化的:

\[Ans_k=\sum\limits_{S \subseteq [n]} [|S|=k] \prod\limits_{i \in S} a_i \]

这个巧妙的意义使它很容易在题目中出现:

另外,拓展的韦达定理也可以这样来理解。

接下来我们把重心放在这个模型的变式上:更多限制,或者说更多维度。

变式 1:Min / Max

Open Cup XXI / Gym102978F Find the LCA 为例,推到最后要求:

\[Ans_k=\sum\limits_{S \subseteq [n]} [|S|=k] (\min S) \prod\limits_{i \in S} a_i \]

想法也很简单,直接暴力枚举最小值:

\[Ans_k=[x^k]\sum\limits_{i=1}^n a_ix \prod\limits_{j=i+1}^n (1+a_jx) \]

看上去很不可做,但实际上也可以分治 + FFT,\(O(n \log^2 n)\)

变式 2:高维权值

P5326 [ZJOI2019]开关 为例(尽管这道题并不需要也不是重点)。

  • 给定集合 \(S\),求:

  • \[Ans_k=\sum\limits_{T \neq \empty} [2 \nmid |S \bigcap T|][\sum\limits_{i \in T} w_i=k] 1 \]

这里实际上有点不太一样,是 有体积的背包 并且 \(a_i=1\),不过大同小异。

可以发现这里有两个条件,上面的模型看似无法扩展。

我们可以尝试给每个变量的权值再加上一维,来满足额外的条件。

这里要数 \(S\)\(T\) 都有的个数,就设权值 \(b_i=[i \in S]。\)

\[Ans_k=\sum\limits_{2 \nmid t} [x^ky^t]\prod\limits_{i=1}^n (1+x^{w_i}y^{b_i}) \]

高维多项式不好处理,但发现只需要计算 \(y\) 的次数为奇数的系数,可以用单位根反演。

\(y=-1\)\(y=1\),得到:

\[Ans_k=[x^k]\dfrac{\prod\limits_{i=1}^n (1+x^{w_i}) - \prod\limits_{i=1}^n (1+x^{w_i}(-1)^{b_i}) }{2} \]

\(O(n \log^2 n)\)


异或之和

  • 给出 \(n\) 个数 \(a_1,a_2,\cdots,a_n\) ,对所有 \(k \in [0,m]\) 求所有任选 \(k\) 个数的异或之和。

  • \(n,m \leq 10^5\)

首先显然每一位是相互独立的,所以可以拆位,现在问题变成了权值只有 \(0\)\(1\)

现在问题变成了权值只有 \(0\)\(1\)

那若干个数的异或和为 \(1\) 当且仅当有奇数个数的权值为 \(1\),只要算出方案数就好了

和刚才的 高维权值 差不多,设权值 \(b_i=w_i\)

\[Ans_k=\sum\limits_{2 \nmid t} [x^ky^t]\prod\limits_{i=1}^n (1+x^{w_i}y^{b_i}) \]

\[Ans_k=[x^k]\dfrac{\prod\limits_{i=1}^n (1+x^{w_i}) - \prod\limits_{i=1}^n (1+x^{w_i}(-1)^{b_i}) }{2} \]

完全一样的,不再解释了。

另:可以拓展至 \(w\) 进制的异或和,对每一种答案做一遍即可,\(O(wn \log^2 n)\)

2.4 有序背包

我们的背包刚才是一种一种选的,不考虑顺序.

如果考虑顺序,相当于每种方案乘上了一个可重排列。

好像正好和指数型生成函数的乘法对应上了!

\[(a_1+a_2+\cdots+a_n)^k=k![x^k]\prod\limits_{i=1}^n e^{a_ix}=k![x^k] e^{\sum_{i=1}^n a_i x} \]

同时这也对应着多项式定理

又是乘法的形式,只不过是 EGF 的。

可以用在 Matrix-Tree 定理上:P5296 [北京省选集训2019]生成树计数

更好玩的来了:我们完全可以把它套在 01 背包 的模型上!

U164351 繁星

\[Ans_{k,p}=\sum\limits_{S \subseteq \{1,2,\cdots,n\}} [|S|=k] (\sum\limits_{i \in S} a_i)^p \]

直接套,得到:

\[Ans_{k,p}=p![x^ky^p]\prod\limits_{i=1}^n (1+e^{a_iy}x) \]

不考虑 \(k\) 的话,即令 \(x=1\),可以通过一系列技巧做到 \(O(\sum a_i \log^2 \sum a_i)\)

2.5. 和之积

\[\prod\limits_{i=1}^n (a+b_i)=[x^n] \dfrac{1}{1-ax} \prod\limits_{i=1}^n (1+b_ix) \]

类似二项式定理拓展的东西。

其实就是从 \(n\) 个括号里选若干个 \(b_i\),剩下的用 \(a\) 补齐。

考虑用多项式的乘积来表示,将 \(a\) 单独用一个多项式 \(1+ax+a^2x^2+\cdots\) 表示;\(b_i\) 由于不能重复,用 \(n\) 个多项式 \(1+b_ix\) 表示:

\[\prod\limits_{i=1}^n (a+b_i)=[x^n] (\sum\limits_{i=0}a^ix^i) \prod\limits_{i=1}^n (1+b_ix)=[x^n] \dfrac{1}{1-ax} \prod\limits_{i=1}^n (1+b_ix) \]

题目:GYM102978D Do Use FFT,非常巧妙地用这个式子化简,从中也可以看到将朴素问题转化为多项式表示的优势。

3. 应用-树上划分类问题

序列划分由于不涉及生成函数不列在此,可见 浅谈划分类问题

划分类问题,即将序列或图分割成若干部分,求所有划分方式的权值运算结果(和,积或最值等)的问题。

给定一棵树,将树划分为若干联通块。形式化的,定义划分为 \(P=\{V_1,V_2,\cdots,V_k\}\),其中 \(V_i\) 代表某个联通块的点集。

由于无法枚举联通块,需要用类似树形依赖背包的思路来做,并用导数优化。

下面就以划分积和为例,略过划分和。

3.1 联通块积和

P5206 [WC2019]数树

  • 给定一棵大小为 \(n\) 的树,定义划分 \(P=\{V_1,V_2,\cdots,V_k\}\) 的权值为:

\[w(P)=\prod\limits_{i=1}^k|V_i| \]

  • 求所有划分的权值之和。

  • \(n \leq 10^5\)

\(f_{u,i}\) 表示以 \(u\) 为根的子树中,根所在的联通块大小为 \(i\)不计算根所在联通块贡献的状态。

考虑转移过程,将子树 \(v\) 加入子树 \(u\) 中:

  • \((u,v)\) 不连,状态不变并计算 \(v\) 的贡献。
  • \((u,v)\) 连,状态改变并不计算贡献。

\[f_{u,i} \leftarrow f_{u,i} \sum\limits_{j=0}^n jf_{v,j} + \sum\limits_{j=0}^i f_{u,j}f_{v,i-j} \]

预处理令 \(f_{u,1}=1\)

朴素计算是 \(O(n^3)\) 的。

用个小 trick,枚举时只枚举到 \(size_u\),可以做到 \(O(n^2)\)

我不满意!

这个形式非常的卷积,尝试把它写成生成函数。

  • \[F_u(x) \leftarrow F_u(x)F_v'(1)+F_u(x)F_v(x) \]

再维护一下导数:

  • \[F_u'(x) \leftarrow F_v'(1)F_u'(x)+F_u'(x)F_v(x)+F_u(x)F_v'(x) \]

最后要求的就是 \(F_1'(1)\),所以其实只需要维护 \(x=1\) 即可。

  • \[F_u(1) \leftarrow F_u(1)F_v'(1)+F_u(1)F_v(1) \]

  • \[F_u'(1) \leftarrow F_v'(1)F_u'(1)+F_u'(1)F_v(1)+F_u(1)F_v'(1) \]

直接做到了 \(O(n)\),非常成功。

当然其实导数就是计算根所在联通块贡献的状态,似乎也可以直接理解跳过生成函数但是我理解不了

3.2 复杂变式

  • 给定一棵大小为 \(n\) 的树,\(m\) 次多项式 \(P(x)\)\(t\),每个点有点权 \(w_i\),定义划分 \(P=\{V_1,V_2,\cdots,V_k\}\) 的权值为:

\[w(P)=\prod\limits_{i=1}^k P(\sum\limits_{u \in V_i} w_u)t^{\sum\limits_{u \in V_i} w_u} \]

  • 求所有划分的权值之和。

  • \(n \leq 10^5\)\(m \leq 10\)

这个问题与朴素的 3.1 相比复杂了很多,不过实际上可以拆开来考虑。

首先后面那一小块其实把 3.1 中的 \(1\) 换成 \(t\) 即可。带权只需要在预处理时考虑即可(\(f_{u,w_u}=1\)),剩下只需要分别维护 \(1 \sim m\) 次导即可。

\[f_{u,i} \leftarrow f_{u,i} \sum\limits_{j} P(j)t^{j}f_{v,j} + \sum\limits_{j} f_{u,j}f_{v,i-j} \]

先处理一下 \(P(x)\),设 \(\sum\limits_{i} P(i)g_ix^i=\sum\limits_{i=0}^m q_kG^{(i)}(x)\)

  • \[F_u(x) \leftarrow F_u(x)\sum\limits_{i=0}^m q_iF_v^{(i)}(t)+F_u(x)F_v(x) \]

由莱布尼茨公式:

\[[F(x)G(x)]^{(k)}=\sum\limits_{i=0}^k \dbinom{k}{i} F^{(i)}(x) G^{(k-i)}(x) \]

于是可以维护 \(k\) 次导:

  • \[F_u^{(k)}(x) \leftarrow F_u^{(k)}(x)\sum\limits_{i=0}^m q_iF_v^{(i)}(t)+\sum\limits_{i=0}^k \dbinom{k}{i} F_u^{(i)}(x) F_v^{(k-i)}(x) \]

把所有 \(x\) 换成 \(t\) 就可以直接转移了,时间复杂度 \(O(nm^2)\)

我不相信这个都还能够考虑意义来做了?


T169751 Nephren | Nephren 题解 - LawArthur&小落的博客

将连通块个数拆成选中点减去选中边,再用期望的线性计算,可以得到大小为 \(k\) 的连通块的贡献实际是一个确定的二次函数。

3.3 多元变式

以二元为例。

  • 给定一棵大小为 \(n\) 的树,每个点有点权 \(a_i,b_i\),定义划分 \(P=\{V_1,V_2,\cdots,V_k\}\) 的权值为:

\[w(P)=\prod\limits_{i=1}^k (\sum\limits_{u \in V_i} a_u)(\sum\limits_{u \in V_i} b_u) \]

  • 求所有划分的权值之和。

  • \(n \leq 10^5\)

这次有两个变量,因此需要多设一维。类似的,设状态为 \(f_{u,i,j}\)

\[f_{u,i,j} \leftarrow f_{u,i,j} \sum\limits_{p} \sum\limits_{q} p q f_{v,p,q} + \sum\limits_{p} \sum\limits_{q} f_{u,p,q}f_{v,i-p,j-q} \]

类似地定义求导 \(\dfrac{\partial}{\partial x\partial y}F(x,y)=\sum\limits_p \sum\limits_q pqf_{p,q}x^{p-1}y^{q-1}\)

我不知道这叫啥……但貌似就是对两元依次求一次偏导,貌似写成 \(\frac{\partial}{\partial x\partial y}\)

\[F_u(x,y) \leftarrow F_u(x,y) \dfrac{\partial}{\partial x\partial y}F_v(1,1)+F_u(x,y)F_v(x,y) \]

乘法公式稍微麻烦一点,可以先对一维求偏导:

\[\dfrac{\partial}{\partial x\partial y}(FG)=\dfrac{\partial}{\partial x}F\dfrac{\partial}{\partial x\partial y}G+\dfrac{\partial}{\partial x}G\dfrac{\partial}{\partial x\partial y}F \]

所以还需要维护 \(\dfrac{\partial}{\partial x}F(x,y)\)

\[\dfrac{\partial}{\partial x}F_u(x,y) \leftarrow \dfrac{\partial}{\partial x}F_u(x,y) \dfrac{\partial}{\partial x\partial y}F_v(1,1)+\dfrac{\partial}{\partial x}F_u(x,y)F_v(x,y)+F_u(x,y)\dfrac{\partial}{\partial x}F_v(x,y) \]

\[\dfrac{\partial}{\partial x\partial y}F_u(x,y) \leftarrow \dfrac{\partial}{\partial x\partial y}F_u(x,y) \dfrac{\partial}{\partial x\partial y}F_v(1,1)+\dfrac{\partial}{\partial x}F_u(x,y)\dfrac{\partial}{\partial x\partial y}F_v(x,y)+\dfrac{\partial}{\partial x\partial y}F_u(x,y)\dfrac{\partial}{\partial x}F_v(x,y) \]

这个式子太长了,统一写成 dp 式好看点:

  • \[f_u \leftarrow f_uh_v+f_uf_v \]

  • \[g_u \leftarrow 2g_uh_v+f_ug_v \]

  • \[h_u \leftarrow h_uh_v+g_uh_v+h_ug_v \]

于是完备了,时间复杂度 \(O(3n)\)

那么同理,\(k\) 元变式的时间复杂度是 \(O(kn)\)

3.4 更复杂的多元变式

T169751 Nephren 启发而来。

  • 给定一棵大小为 \(n\) 的树,每个点有点权 \(w_i,p_i\)
  • 对于点集 \(T \subseteq S\) 定义 \(C(S,T)\) 表示点集 \(S\)\(T\) 形成的联通块个数,定义划分 \(P=\{V_1,V_2,\cdots,V_k\}\) 的权值为:

\[w(P)=\sum\limits_{T} \prod\limits_{i=1}^k C(V_i,V_i \bigcap T)(\sum\limits_{i \in V_i}w_i)(\prod\limits_{i \in T}p_i) \]

  • 求所有划分的权值之和。

  • \(n \leq 10^5\)

实际上就是把 T169751 Nephren 加了个点权,所以现在每个联通块的贡献不再相同,需要考虑多记录一维。

下面称 \(T\) 中的点为关键点。

直接考虑 \(f_{u,i,j,t}\) 分别表示根所在联通块的 \(w\) 权值之和为 \(i\),关键点联通块个数为 \(j\),根是否为关键点,不统计根所在联通块的答案。需要注意当两个根都是关键点时,联通块数量要减 \(1\)

\[f_{u,i,j,0} \leftarrow f_{u,i,j,0} \sum\limits_{p} \sum\limits_{q} pq (f_{v,p,q,0}+f_{v,p,q,1})+\sum\limits_{p} \sum\limits_{q} f_{u,p,q,0} (f_{v,i-p,j-q,0}+f_{v,i-p,j-q,1}) \]

\[f_{u,i,j,1} \leftarrow f_{u,i,j,1} \sum\limits_{p} \sum\limits_{q} pq (f_{v,p,q,0}+f_{v,p,q,1})+\sum\limits_{p} \sum\limits_{q} f_{u,p,q,1} (f_{v,i-p,j-q,0}+f_{v,i-p,j-q+1,1}) \]

仿照 2.3 改写,为了板式这里就只写函数名了。

\[F0_u \leftarrow F0_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+F0_u(F0_v+F1_v) \]

\[F1_u \leftarrow F1_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+F1_u(F0_v+\dfrac{1}{y}F1_v) \]

仍然维护 \(\dfrac{\partial}{\partial x}\)\(\dfrac{\partial}{\partial y}\)\(\dfrac{\partial}{\partial x\partial y}\)

\[\dfrac{\partial}{\partial x}F0_u \leftarrow \dfrac{\partial}{\partial x}F0_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+\dfrac{\partial}{\partial x}F0_u(F0_v+F1_v)+F0_u(\dfrac{\partial}{\partial x}F0_v+\dfrac{\partial}{\partial x}F1_v) \]

\[\dfrac{\partial}{\partial x}F1_u \leftarrow \dfrac{\partial}{\partial x}F1_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+\dfrac{\partial}{\partial x}F1_u(F0_v+\dfrac{1}{y}F1_v)+F1_u(\dfrac{\partial}{\partial x}F0_v+\dfrac{1}{y}\dfrac{\partial}{\partial x}F1_v) \]

\[\dfrac{\partial}{\partial y}F0_u \leftarrow \dfrac{\partial}{\partial y}F0_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+\dfrac{\partial}{\partial y}F0_u(F0_v+F1_v)+F0_u(\dfrac{\partial}{\partial y}F0_v+\dfrac{\partial}{\partial y}F1_v) \]

\[\begin{aligned} \dfrac{\partial}{\partial y}F1_u & \leftarrow \dfrac{\partial}{\partial y}F1_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+\dfrac{\partial}{\partial y}F1_u(F0_v+\dfrac{1}{y}F1_v)+F1_u(\dfrac{\partial}{\partial y}F0_v+\dfrac{\partial}{\partial y}\dfrac{1}{y}F1_v)\\ & =\dfrac{\partial}{\partial y}F1_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+\dfrac{\partial}{\partial y}F1_u(F0_v+\dfrac{1}{y}F1_v)+F1_u(\dfrac{\partial}{\partial y}F0_v+\dfrac{1}{y}\dfrac{\partial}{\partial y}F1_v-\dfrac{1}{y^2}F1_v) \end{aligned} \]

\[\dfrac{\partial}{\partial x\partial y}F0_u \leftarrow \dfrac{\partial}{\partial x\partial y}F0_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+\dfrac{\partial}{\partial x\partial y}F0_u(F0_v+F1_v)+F0_u(\dfrac{\partial}{\partial x\partial y}F0_v+\dfrac{\partial}{\partial x\partial y}F1_v)+\dfrac{\partial}{\partial x}F0_u(\dfrac{\partial}{\partial y}F0_v+\dfrac{\partial}{\partial y}F1_v)+\dfrac{\partial}{\partial y}F0_u(\dfrac{\partial}{\partial x}F0_v+\dfrac{\partial}{\partial x}F1_v) \]

\[\begin{aligned} \dfrac{\partial}{\partial x\partial y}F1_u & \leftarrow \dfrac{\partial}{\partial x\partial y}F1_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)]+\dfrac{\partial}{\partial x\partial y}F1_u(F0_v+\dfrac{1}{y}F1_v)+F1_u(\dfrac{\partial}{\partial x\partial y}F0_v+\dfrac{\partial}{\partial x\partial y}\dfrac{1}{y}F1_v)+\dfrac{\partial}{\partial x}F1_u(\dfrac{\partial}{\partial y}F0_v+\dfrac{\partial}{\partial y}\dfrac{1}{y}F1_v)+\dfrac{\partial}{\partial y}F1_u(\dfrac{\partial}{\partial x}F0_v+\dfrac{\partial}{\partial x}\dfrac{1}{y}F1_v)\\ & =\dfrac{\partial}{\partial x\partial y}F1_u[\dfrac{\partial}{\partial x\partial y}F0_v(1,1)+\dfrac{\partial}{\partial x\partial y}F1_v(1,1)] +\dfrac{\partial}{\partial x\partial y}F1_u(F0_v+\dfrac{1}{y}F1_v) +F1_u(\dfrac{\partial}{\partial x\partial y}F0_v+\dfrac{1}{y}\dfrac{\partial}{\partial x\partial y}F1_v-\dfrac{1}{y^2}\dfrac{\partial}{\partial x}F1_v) +\dfrac{\partial}{\partial x}F1_u(\dfrac{\partial}{\partial y}F0_v+\dfrac{1}{y}\dfrac{\partial}{\partial y}F1_v-\dfrac{1}{y^2}F1_v) +\dfrac{\partial}{\partial y}F1_u(\dfrac{\partial}{\partial x}F0_v+\dfrac{1}{y}\dfrac{\partial}{\partial x}F1_v) \end{aligned} \]

已经完备,时间复杂度 \(O(n)\)

4. 拓展-生成函数常见技巧

一些技巧可以神奇地使 \(\log^2\) 变成 \(\log\)\(\log\) 变成线性。

4.1 多项式复合简单函数

  • 给出 \(n\) 次多项式 \(F(x)\) 及函数 \(G(x)\),要求快速计算 \(S(x)=F(G(x))\)

  • 幂函数:\(G(x)=x^a\)

就是 1.2 平移、伸缩与单位根反演 中的伸缩变换。

  • 一次函数:\(G(x)=ax+b\)

\[\begin{aligned} F(G(x)) &= \sum\limits_{i=0}^n f_i (ax+b)^i\\ &=\sum\limits_{i=0}^n f_i \sum\limits_{j=0}^i \dbinom{i}{j} a^j b^{i-j} x^j\\ \end{aligned} \]

\[\begin{aligned} s_j &=\sum\limits_{i=j}^n \dbinom{i}{j} a^j b^{i-j} f_i\\ &=\dfrac{1}{j!} a^j \sum\limits_{i=j}^n i!f_i \dfrac{b^{i-j}}{(i-j)!}\\ \end{aligned} \]

做一次差卷积即可。时间复杂度 \(O(n \log n)\)

  • 二次函数:\(G(x)=ax^2+bx+c\)

\[G(x)=a(x+\dfrac{b}{2a})^2+c-\dfrac{b^2}{4a} \]

先复合 \(x+\dfrac{b}{2a}\),再复合 \(x^2\),再复合 \(ax+c-\dfrac{b^2}{4a}\) 即可。时间复杂度 \(O(n \log n)\)

  • 高次多项式:\(G(x)=\sum\limits_{i=0}^k g_ix^i(k \geq 3)\)

无法再像二次函数一样表示成若干形如 \(x^c\)\(x+c\) 的复合。

可以直接使用分治乘计算 \(\sum\limits_{i=0}^n f_i G^i(x)\),复杂度 \(O(nk \log^2 (nk))\)

也可以用多项式复合的做法。

  • 特殊一次分式:\(G(x)=\dfrac{1}{1-ax}\)

\[\begin{aligned} F(G(x)) &=f_0+\sum\limits_{i=1}^n f_i \dfrac{1}{(1-ax)^i}\\ &=f_0+\sum\limits_{i=1}^n f_i \sum\limits_{j=0}^{+ \infty} \dbinom{i-1+j}{i-1} a^j x^j\\ \end{aligned} \]

\[\begin{aligned} s_j &=[j=0]f_0+\sum\limits_{i=1}^n \dbinom{i-1+j}{j} a^j f_i\\ &=[j=0]f_0+\dfrac{1}{j!} a^j \sum\limits_{i=1}^n \dfrac{f_i}{(i-1)!} (i-1+j)!\\ &=[j=0]f_0+\dfrac{1}{j!} a^j \sum\limits_{i=0}^{n-1} \dfrac{f_{i+1}}{i!} (i+j)!\\ \end{aligned} \]

做一次差卷积即可。时间复杂度 \(O(n \log n)\)

  • 一次分式:\(G(x)=\dfrac{ax+b}{cx+d}\)

\[G(x)=\dfrac{a}{c}+\dfrac{b-\dfrac{ad}{c}}{cx+d}=\dfrac{a}{c}+\dfrac{bc-ad}{cd} \dfrac{1}{1+\dfrac{c}{d}x} \]

先复合 \(\dfrac{1}{1+\dfrac{c}{d}x}\),再复合 \(\dfrac{bc-ad}{cd} x+\dfrac{a}{c}\) 即可。时间复杂度 \(O(n \log n)\)

  • 指数函数:\(G(x)=ae^x\)

\[\begin{aligned} F(G(x)) &= \sum\limits_{i=0}^n f_i a^i e^{ix}\\ &=\sum\limits_{i=0}^n a^i f_i \sum\limits_{j=0}^{+ \infty} i^j \dfrac{x^j}{j!}\\ \end{aligned} \]

\[s_j=\dfrac{1}{j!} \sum\limits_{i=0}^n i^j a^i f_i \]

\(p_j=j! s_j\),写成生成函数:

\[P(x)=\sum\limits_{j} \sum\limits_{i=0}^n i^j a^i f_i x^j=\sum\limits_{i=0}^n a^i f_i \sum\limits_{j} i^j x^j=\sum\limits_{i=0}^n \dfrac{a^i f_i}{1-ix} \]

可以直接分治乘,时间复杂度 \(O(n \log^2 n)\)

实际上在 1.5 数列 k 次幂和 中已经提到过。

4.2 线性算法与转置原理

本节不再是形式上的技巧,可能涉及到算法本身。

  • 线性算法:本质为一个 \(n \times m\) 的矩阵 \(M\)。输入长为 \(n\) 的列向量 \(v\),输出长为 \(m\) 的列向量 \(Mv\)

线性算法在 OI 中很常见,如 \(\text{FFT,FWT}\),各种反演以及大部分 DP。

在线性算法中,只会用到三种指令:

  • swap i j,交换变量 \(i,j\) 的值。
  • mul i c,令 \(i \leftarrow ci\)
  • add i j c,令 \(j \leftarrow j+ci\)

这三种指令分别对应矩阵的初等变换:

  • 交换两行或两列。
  • 用常数 \(c\) 乘以某一行。
  • 用常数 \(c\) 乘以某一行加到另一行中去。

  • 转置原理:可以将计算 \(M\) 的线性算法转换成计算 \(M^T\) 的线性算法,乘法次数不变,加法次数比原算法多至多 \(m−n\) 次。

具体而言,倒序考虑每个指令,并如下转换:

  • swap i j:不变。
  • mul i c:不变。
  • add i j c:改为 令 \(i \leftarrow i+cj\)

理解:将转移视为带权边,则产生一张 DAG。定义路径的权为其中所有边权之积。
设初始状态为 \(S\),终止状态为 \(T\)。则 \(T_j\) 的值可视为 \(S_i\)\(T_j\) 的所有路径权值乘上 \(S_i\) 之和,即 \(T_j=\sum\limits_i M_{j,i} S_i\)
故将所有边反向,权值不变,倒着转移可在 \(S\) 处得到 \(S_i=\sum\limits_j M_{j,i} T_j=\sum\limits_j M^T_{i,j} T_j\)

  • 例子:前缀和 \(T_i=\sum\limits_{j=0^i} S_j\)

\[M= \left [ \begin{matrix} 1 & 0 & \cdots & 0 & 0\\ 1 & 1 & \cdots & 0 & 0\\ \vdots & \vdots & \ddots & \vdots & \vdots\\ 1 & 1 & \cdots & 1 & 1\\ \end{matrix} \right ] \]

\[M^T= \left [ \begin{matrix} 1 & 1 & \cdots & 1 & 1\\ 0 & 1 & \cdots & 1 & 1\\ \vdots & \vdots & \ddots & \vdots & \vdots\\ 0 & 0 & \cdots & 0 & 1\\ \end{matrix} \right ] \]

故其转置为后缀和

  • 例子:\(\text{DFT}\) \(T_i=\sum\limits_{j=0^n} \omega_n^{ij} S_j\)

显然其转置为其自身。

  • 例子:多点求值 \(T_i=\sum\limits_{j=0}^n a_i^j S_j\) 与幂和 \(T_j=\sum\limits_{i=0}^n a_i^j S_i\)

后者可以写为生成函数 \(\sum\limits_{i=0}^n \dfrac{S_i}{1-a_ix}\),直接分治乘可做到 \(O(n \log^2 n)\)

转置后可得到小常数的 \(O(n \log^2 n)\) 多点求值算法。

  • 例子:给定 \(n\) 次多项式 \(A(x)\) 及简单函数 \(P(x)\),对每个 \(i \in [0,n]\)\(f_i=[x^n]A(x)P^i(x)\)

\(G(x)=A(P(x))\)

\[f_i=\sum\limits_{j=0}^n a_{n-j} [x^j] P^i(x) \]

\[g_i=\sum\limits_{j=0}^n a_{j} [x^i] P^j(x) \]

于是将 \(A\) 翻转后两个问题是互为转置的(可以用 2.1 的方法解决后者)。

4.3 D-finite 形式幂级数

A holonomic function is a smooth function of several variables that is a solution of a system of linear homogeneous differential equations with polynomial coefficients --Wikipedia

称多项式 \(F(x)\) 为 D-finite 形式幂级数当且仅当 存在非负整数 \(r\) 与多项式 \(a_0(x),a_1(x),\cdots,a_r(x),b(x)\) 满足:

\[\sum\limits_{i=0}^r a_i(x) F^{(i)}(x)=b(x) \]

其中 \(r\) 被称为

D-finite 形式幂级数 包括代数形式幂级数(包括有限次多项式,有限次分式),指数函数 \(e^x\),……

常见例子:

  • 一行组合数:\(\sum\limits_{i=0}^n \dbinom{n}{i}=(1+x)^n\)

  • 一列组合数:\(\sum\limits_{i} \dbinom{i}{n}=\dfrac{1}{(1-x)^{n+1}}\)

  • 斐波那契数 / 卡特兰数:带根号的封闭形式(二次方程根)。

  • 阶乘的生成函数,调和级数,错排。

  • 二维分式的一行:是 Algebraic 的。

性质:若多项式 \(F(x)\)\(G(x)\) 均为 D-finite 的,

  • 可加:\(F(x)\)\(G(x)\) 的线性组合 \(\alpha F(x)+\beta G(x)\) 也为 D-finite 的。

  • 可乘:\(F(x)\)\(G(x)\) 的卷积 \(F(x)G(x)\) 与点积 \(\sum\limits_n f_ng_n x^n\) 也为 D-finite 的。此外 \(F^n(x)\) 也为 D-finite 的。

  • 可积分:\(F(x)\) 的积分 \(\int F(x)\) 也为 D-finite 的。

  • 可复合(代数函数):若 \(a(x)\) 为 Algebraic 的,则 \(F(a(x))\) 也为 D-finite 的(但 \(a(F(x))\) 不一定)。


下面考虑的 D-finite 形式幂级数均为低阶(阶数可视为 \(O(1)\))。

D-finite 复合:给定低阶 D-finite 的 \(F(x)\)\(G(x)\),可在 \(\widetilde O(n)\) 内求出 \(F(G(x))\)

\(F(x)\) 满足 \(\sum\limits_{i=0}^r a_i(x) F^{(i)}(x)=0\)

\[\sum\limits_{i=0}^r a_i(x) F^{(i)}(x)=0 \]

\[\sum\limits_{i=0}^r a_i(G(x)) F^{(i)}(G(x))=0 \]

\(F(G(x))'=G'(x) F'(G(x))\),由莱布尼茨公式可表示 \(F(G(x))^{(t)}\)

\[F(G(x))^{(t)}=\sum\limits_{i=0}^t \dbinom{t}{i} G^{(i)}(x) F^{(t-i)}(G(x)) \]

可反演用 \(F(G(x))^{(t)}\) 表示出 \(F^{(t)}(G(x))\),带回原式解方程即可。

解方程可以将分母的 \(G^{(t)}\) 通分,再分治 NTT 解。

D-finite 复合逆:给定低阶 D-finite 的 \(F(x)\) ,可快速求出 \(G(x)\) 使 \(F(G(x))=x\)

列出上面的方程,同样解即可。


高阶递推:对于阶为 \(r\) 的 D-finite 形式幂级数 \(F(x)\),记录连续的 \(r\) 个系数可以 \(O(r)\) 推出下一项系数。

事实上 D-finite 形式幂级数具有整式递推的形式。

同时,若一列 D-finite 形式幂级数的相邻两项的比值为简单分式,可以由一项的系数快速推到下一项的系数,可以以曼哈顿距离的复杂度从一个位置的值转移到另一个位置(例如莫队形式的组合数前缀和)。

可以线性求出形如 \((1+x)^a(1-x)^b\) 甚至 \(\sum\limits_{i=0}^c \dbinom{d}{i} (1+x)^i(1-x)^{d-i}\) 等幂级数。

类似莫队线性求出形如 \([x^{a-k-1}] (1+x)^a(1-x)^k\) 的幂级数。

4.4 q-模拟

q-analog 是对一般对象组合意义的拓展,一般令 \(q=1\) 即是原对象。

\(q\) 可以是任意对象,但下面仅认为 \(q\)\(\mathbb{F}_p\) 下的整数且满足 \(\text{ord}_q < n\)

定义 q-整数为:

\[[n]_q=\dfrac{1-q^n}{1-q}=1+q+\cdots+q^{n-1} \]

\(q=1\) 时对左式取极限得 \([n]_1=n\),即一般正整数。

定义 q-阶乘 及 q-组合数 为:

\[n!_q=\prod\limits_{i=1}^n [i]_q \]

\[\dbinom{n}{m}_q=\dfrac{n!_q}{m!_q (n-m)!_q} \]

q-binomial 有许多与一般组合数类似的性质,如 q-binomial 均为非负整数,二项式定理,范德蒙德卷积,卢卡斯定理等。这里暂不详细展开。

q-analog 与生成函数

考虑 \(P(x)=\prod\limits_{i=0}^{n} \dfrac{1}{1-q^ix}\)

\[P(qx)=\prod\limits_{i=1}^{n} \dfrac{1}{1-q^i x}=\dfrac{1-x}{1-q^{n+1}x} P(x) \]

\[(1-q^{n+1}x) P(qx)=(1-x) P(x) \]

\[p_i(q^i-1)=p_{i-1}(q^{n+i}-1) \]

这个形式类似 \(i \dbinom{n+i}{n}=(i+n) \dbinom{n+i-1}{n}\),有:

\[[x^i]P(x)=\dbinom{n+i}{n}_q \]

关于正负号,\(P(x)\) 中的系数一定全为正的,而 \(n!_q\) 也为正。

\(P(x)\) 的形式往往在确定向量空间维数时出现,后面会详细展开说明。


类似的,若设 \(P(x)=\prod\limits_{i=0}^{n} (1+q^ix)\),可以得到:

\[(1+q^{n+1}x) P(qx)=(1+x) P(x) \]

\[p_i(q^i-1)=p_{i-1}(q^{n+1}-q^{i-1})=p_{i-1} q^{i-1}(q^{n-i+2}-1) \]

同样类比 \(i \dbinom{n+1}{i}=(n-i+2) \dbinom{n+1}{i-1}\),可得到:

\[[x^i]P(i)=q^{i(i-1)/2} \dbinom{n+1}{i}_q \]

当然这种方法不止于此,涉及到 q-analog 的形式幂级数在解题时往往只需要代入 \(P(qx)\) 即可线性递推。

例题:ARC129F Many Xor Optimization Problems 后半段推导。

q-analog 与线性代数中的计数

下面使用记号 \((\omega;q)_n=(1-\omega)((1-\omega q) \cdots (1-\omega q^{n-1})\),显然 \(\dbinom{n}{m}_q=\dfrac{(q;q)_n}{(q;q)_m (q;q)_{n-m}}\)

问题:求域为 \(\mathbb{F}_p\) 的秩恰为 \(k\)\(n\)\(m\) 维向量组数量。

考虑每个 \(n\)\(m\) 维向量组一定可以进行 \(n\) 次以下步骤之一来唯一得到:

  • 添加一个与已有向量线性有关的向量。假设当前秩为 \(t\),则方案数为 \(2^t\)

  • 添加一个与已有向量线性无关的向量,使秩增加 \(1\)(需要恰好被进行 \(k\) 次)。假设当前秩为 \(t\),则方案数为 \(2^m-2^t\)

写成生成函数即:

\[p_k=\prod\limits_{i=0}^{k-1} (q^m-q^i) [x^{n-k}] \prod\limits_{i=0}^k \dfrac{1}{1-q^ix}=\dbinom{n}{k}_2 \prod\limits_{i=0}^{k-1} (q^m-q^i) \]

问题:求域为 \(\mathbb{F}_p\)\(n\) 维空间中 \(k\) 维线性空间数量。

考虑上一个问题中每个 \(k\) 维线性空间会被计算多少次,相当于计算秩恰为 \(k\)\(n\)\(k\) 维向量组数量,即:

\[\dbinom{n}{k}_q \prod\limits_{i=0}^{k-1} (q^k-q^i) \]

故答案为:

\[\dfrac{\prod\limits_{i=0}^{k-1} (q^n-q^i)}{\prod\limits_{i=0}^{k-1} (q^k-q^i)}=\dfrac{\prod\limits_{i=0}^{k-1} (q^{n-i}-1)}{\prod\limits_{i=0}^{k-1} (q^{k-i}-1)}=\dfrac{(q;q)_n/(q;q)_{n-k}}{(q;q)_k}=\dbinom{n}{k}_q \]

子空间反演

类比二项式反演,有:

\[g_k=\sum\limits_{i=0}^k \dbinom{k}{i}_q f_i \Leftrightarrow f_k=\sum\limits_{i=0}^k (-1)^i q^{\binom{i}{2}} \dbinom{k}{i}_q g_i \]

容易发现 \(q=1\) 时即是二项式反演。

下面是证明,只证从左式推右式。

\[\dfrac{g_k}{(q;q)_k}=\sum\limits_{i=0}^k \dfrac{f_i}{(q;q)_i} \dfrac{1}{(q;q)_{k-i}} \]

定义 q-生成函数形如 \(\sum\limits_{i \geq 0} f_i \dfrac{x^i}{(q;q)_i}\)\(\zeta(x)=\sum\limits_{i \geq 0} \dfrac{x^i}{(q;q)_i}\)

\(G(x)=F(x)\zeta(x)\),所求即 \(F(x)=G(x)\zeta(x)^{-1}\),下面考虑求 \(\zeta(x)^{-1}\)

\[\zeta(x)=\sum\limits_{i \geq 0} \dfrac{x^i}{(q;q)_i} \]

\[\zeta(x)-\zeta(qx)=\sum\limits_{i \geq 0} \dfrac{(q^i-1)x^i}{(q;q)_i}=\sum\limits_{i \geq 1} \dfrac{(q^i-1)x^i}{(q;q)_i}=\sum\limits_{i \geq 1} \dfrac{x^i}{(q;q)_{i-1}}=x \sum\limits_{i \geq 0} \dfrac{x^i}{(q;q)_i}=x \zeta(x) \]

展开得:

\[\zeta_k=q^k \zeta_k-q^{k-1} \zeta_{k-1} \]

\[\zeta_k=-\dfrac{q^{k-1}}{1-q^k} \zeta_{k-1}=(-1)^k q^{\binom{k}{2}} \dfrac{1}{(q;q)_k} \]

故得:

\[f_k=\sum\limits_{i=0}^k (-1)^i q^{\binom{i}{2}} \dbinom{k}{i}_q g_i \]

5. 解析组合与简单无标号组合构造

终于进入正题了!

本文中 组合对象符号化 部分参考多项式计数杂谈 - command_block

5.1 组合类

组合类是一类东西的集合,比如说若干有根无标号树或者若干堆蜜蜂的集合。


用好看的大写字母 \(\mathcal A\) 表示。

组合类内的每个元素都定义了一个大小,比如树的大小就是它的节点个数,一群蜜蜂的大小就是里面蜜蜂的数量。

记元素 \(\alpha \in \mathcal A\),则其大小为 \(|\alpha|\),需要满足:

  • \(|\alpha| \in N\)
  • \(\forall n \in N\) ,以其为大小的元素有限。

\(\mathcal{A}_k\) 表示 \(\mathcal{A}\) 中大小为 \(k\) 的元素集合,即 \(\mathcal{A}_k={a|a \in A,|a|=k}\)

组合类的生成函数

如果记 \(\mathcal A\) 中大小为 \(n\) 的元素有 \(a_n\) 个,那它的生成函数为:

\[A(x)=\sum\limits_{i=1}^n a_ix^i \]

5.2 笛卡尔积

假如我们有一棵 \(5\) 个节点的树和 \(2\) 只蜜蜂。

我们把它们放在一起,会得到啥呢?

组成了新的元素:带树的蜜蜂,大小为 \(7\)

组成了新的元素:带蜜蜂的树,大小也为 \(7\)

这就叫笛卡尔积,相当于将 树和蜜蜂 组成一个有序对,大小为二者的和。

现在我们有了元素的笛卡尔积了,那集合的笛卡尔积也比较优美:

\[\mathcal A \times \mathcal B=\mathcal C=\{\gamma=(\alpha,\beta)|\alpha \in \mathcal A,\beta \in \mathcal B \} \]


现在来考虑它的生成函数。

聪明的同学肯定又发现了:这就是卷积!

\[C(x)=A(x)B(x) \]

有个小问题:\(A(x)B(x)=B(x)A(x)\) ,是有序的吗?

是有序的。这两个虽然结果一样,但是表示的东西截然不同。

就像带树的蜜蜂和带蜜蜂的树完全不一样(但是大小是一样的)。

  • 笛卡尔积的意义是组合,也可以理解为分步计数

5.3 和(不交并)

假如我们有一棵 \(5\) 个节点的树和 \(5\) 只蜜蜂。

我们还是把它们都拿出来,但这次放得比较远:



没有组成新的元素。

其实就相当于将二者列出来,而不是组合起来。

如果 \(\mathcal {A \bigcap B = \emptyset}\) (要求不交是为了方便运算),

定义 \(\mathcal A + \mathcal B = \mathcal A \bigcup \mathcal B = C\)

显然有 \(A(x)=B(x)+C(x)\)

只有相同大小的才会被加到一块,就没有问题了。

  • 不交并的意义是分类,也可以理解为分类计数。

5.4 简单应用

一个比较简单的例子:

  • 现在有 \(2\) 只黄色的蜜蜂,\(5\) 只蓝色的蜜蜂和 \(3\) 只红色的蜜蜂,同种蜜蜂之间不区分不区分顺序 ,从中选出 \(7\) 只有多少方法?

考虑一种一种地选。

黄色蜜蜂(组合类)的生成函数就是 \(Y(x)=1+x+x^2\)

【意思就是,选 \(0\) 只黄色蜜蜂有一种方法,选 \(1\) 只黄色蜜蜂有一种方法,选 \(2\) 只黄色蜜蜂也有一种方法】

同理蓝色蜜蜂的生成函数就是 \(B(x)=1+x+x^2+x^3+x^4+x^5\)

红色蜜蜂的生成函数就是 \(R(x)=1+x+x^2+x^3\)

把它们乘起来,多项式的 \(x^7\) 项就是答案。

也许有一个问题,笛卡尔积不是有序的吗?

当然。所以这里是先选黄色蜜蜂,再选蓝色蜜蜂,最后选红色蜜蜂。

5.5 Sequence(序列)构造

我们有一个组合类 \(\mathcal A\) ,用一只蜜蜂来表示:

现在让 \(\mathcal {B=A \times A}\),用两只蜜蜂表示:

现在再让 \(\mathcal {C=A \times A \times A}\),用三只蜜蜂表示:


\(\cdots\)

我们把它们排成一列,相加起来。

得到了啥?

\[\operatorname{SEQ}(\mathcal A)=\mathcal{\{E\}+A+A \times A + A \times A \times A + \cdots} \]

\(\{\mathcal E\}\) 也是一个组合类,不过它里面只有一个元素,叫空集。

这是啥意思呢?

加的意思是

也就是说,从 \(\operatorname{SEQ}(\mathcal A)\) 取一个元素出来,可能是一只单独的蜜蜂,也可能是几只蜜蜂聚在一起,也可能是一个大蜂巢(有顺序)。

生成函数也是一样滴:

\[\operatorname{SEQ}(A(x))=1+A(x)+A(x)^2+A(x)^3+\cdots=\dfrac{1}{1-A(x)} \]

5.6 Amplification(膨胀)构造

从组合类里任意取出一群蜜蜂,然后把它们复制成 \(k\) 份组合在一起,再放回去。

每个元素本质上没有变化,只是大小变成了 \(k\) 倍。

\[\operatorname{AMP}_k(\mathcal{A})=\{(a,a,\cdots,a)|a \in \mathcal{A}\} \]

生成函数就是将 \(x\) 替换成 \(x^k\)

\[\operatorname{AMP}_k(A(x))=A(x^k) \]

5.7 试试看!

  • \(n\) 个点的无标号有根树数量,区分子树(即将两棵子树交换后视为不同)。

  • \(n \leq 10^5\)


记组合类为 \(\mathcal A\),先把根提出来。

等下,子树有多少个?并不知道啊!

啊,刚刚的序列就是解决这个问题的:

\(A(x)=x\operatorname{SEQ}(A(x))=\dfrac{x}{1-A(x)}\)


这东西怎么解呢?

\[A(x)-A(x)^2-x=0 \]

牛顿迭代即可……或者直接解方程?\(O(n \log n)\)

6. 群论与无标号组合构造

接下来的几章可能较为深入,如果觉得有困难可以先跳过。

(可能可以一直跳到第 \(8\) 章)

广告!Burnside 引理与 Polya 定理

6.1 置换群上的组合类

设组合类 \(\mathcal{A}\) 的每一个元素 \(a\) 都可以被表示为另一个组合类 \(\mathcal{B}\) 中若干元素的有序组合

\[a=(b_1,b_2,\cdots,b_m),b_i \in \mathcal{B} \]

这里的 \(m\) 被称为 \(a\) 的容,记作 \(\operatorname{cap}_{\mathcal{B}}(a)=m\)

注意大小不是一个东西,并不一样。

举个例子,可以定义有根无标号树的容是根节点的子树棵数,但大小是整棵树的点数。

这个有序组合相当于给每个 \(a\) 染上色,大多数问题中 \(\mathcal{B}\)\(\mathcal{A}\) 是相同的

接下来定义置换群列 \(G=\{G_0,G_1,G_2,\cdots\}\),其中 \(G_k\) 是包含大小为 \(k\) 的置换的置换群。

于是群论的一系列东西都可以用了。

\(\mathcal{A}\) 放到 \(G\) 上,得到轨道:

\[\mathcal{A}/G_{\mathcal{B}}=\{G_{|a|} \cdot a|a \in \mathcal{A}\} \]

也就是组合类的组合类了。

它的生成函数的计算只关心个数,可以用 Burnside 引理,接下来会详细介绍。

6.2 Cycle(循环)构造

\(G\) 为所有循环构成的置换群列。

\[\operatorname{CYC}(\mathcal{A})=(SEQ(\mathcal{A})-\{\mathcal{E}\})/G_{\mathcal{A}} \]

(注意这里 \(\mathcal{B}\)\(\mathcal{A}\) 是相同的)

\(\mathcal{A}\) 的元素想象成珠子,就是对项链去重。

先分析容为 \(k\) 的元素,记:

\[\operatorname{CYC}_k(\mathcal{A})=(\mathcal{A}^k)/G_{\mathcal{A}} \]

考虑计算它的生成函数,用 Burnside 引理。

对于旋转 \(i\) 次的置换,会产生 \(\gcd(i,k)\) 个等价类,每个等价类的 \(k/ \gcd(i,k)\) 个元素必须相同。

\[\operatorname{CYC}_k(A(x))=\dfrac{1}{k} \sum\limits_{i=0}^{k-1} A(x^{k/ \gcd(i,k)})^{\gcd(i,k)} \]

然后枚举 \(\gcd(i,k)\)

\[\operatorname{CYC}_k(A(x))=\dfrac{1}{k} \sum\limits_{d|k} \varphi(\frac{k}{d}) A(x^{\frac{k}{d}})^d=\dfrac{1}{k} \sum\limits_{d|k} \varphi(d) A(x^d)^{\frac{k}{d}} \]

接下来求 \(CYC(A(x))\)

\[\operatorname{CYC}(A(x))=\sum\limits_{k \geq 1} \dfrac{1}{k} \sum\limits_{d|k} \varphi(d) A(x^d)^{\frac{k}{d}} \]

交换和号:

\[\operatorname{CYC}(A(x))=\sum\limits_{d \geq 1} \sum\limits_{d|k} \dfrac{1}{k} \varphi(d) A(x^d)^{\frac{k}{d}} \]

\(k=dt\)

\[\operatorname{CYC}(A(x))=\sum\limits_{d \geq 1} \dfrac{\varphi(d)}{d} \sum\limits_{t \geq 1} \dfrac{A(x^d)^t}{t} \]

啊哈,后面那个和号正好是 \(\ln(\dfrac{1}{1-x})\) 的泰勒展开。

\[\operatorname{CYC}(A(x))=\sum\limits_{d \geq 1} \dfrac{\varphi(d)}{d} \ln(\dfrac{1}{1-A(x^d)}) \]

\(F(x)=\ln(\dfrac{1}{1-A(x)})\),显然可以快速算出。

\[\operatorname{CYC}(A(x))=\sum\limits_{d \geq 1} \dfrac{\varphi(d)}{d} F(x^d) \]

于是就可以在 \(O(n \log n)\) 的时间计算出 \(CYC(A(x))\)

6.3 Multiset(可重集)构造

也叫 Euler 变换,记作 \(\mathcal{E(A)}\)

\(G\) 为所有置换构成的置换群列。

\[\operatorname{MSET}(\mathcal{A})=\operatorname{SEQ}(\mathcal{A})/G_{\mathcal{A}} \]

(注意这里 \(\mathcal{B}\)\(\mathcal{A}\) 也是相同的)

也记为 \(\operatorname{Exp}(A(x))\)

实际上就是各元素不考虑顺序地拼起来。

计算生成函数,直接考虑方案(即组合类内的一个元素)。

比如现在考虑大小为 \(i\) 的一种方案,当然这样的方案有 \(a_i\) 种。

考虑它可以选几次,当然选多少次都行。

\[1+x^i+x^{2i}+\cdots=\dfrac{1}{1-x^i} \]

这样的方案有 \(a_i\) 种,其实就是全部乘起来:

\[(\dfrac{1}{1-x^i})^{a_i}=\dfrac{1}{(1-x^i)^{a_i}} \]

再考虑上所有的大小:

\[\operatorname{MSET}(A(x))=\prod\limits_i \dfrac{1}{(1-x^i)^{a_i}} \]

这个东西没办法快速计算,请出第二定义!

\[\operatorname{MSET}(A(x))=\exp(\sum\limits_i \dfrac{A(x^i)}{i}) \]

这里写一写多项式推导的证明:

\[\operatorname{MSET}(A(x))=\prod\limits_i (1-x^i)^{-a_i} \]

首先看到这个 \(f_i\) 很不好,取个对数。

\[\operatorname{MSET}=\exp(\sum\limits_i -a_i \ln(1-x^i)) \]

然后由前置知识,可以化开来:

\[\operatorname{MSET}(A(x))=\exp(\sum\limits_i a_i\sum\limits_{j \geq 1}\dfrac{x^{ij}}{j}) \]

换个和号,

\[\operatorname{MSET}(A(x))=\exp(\sum\limits_{j \geq 1} \dfrac{1}{j} \sum\limits_{i} a_ix^{ij}) \]

大功告成!

\[\operatorname{MSET}(A(x))=\exp(\sum\limits_{j \geq 1} \dfrac{A(x^j)}{j} ) \]

已经可以在 \(O(n \log n)\) 的时间内求出了。

还有一个拿 Burnside 引理和 Pólya 定理推的,可以去看看这篇:


等一下,是不是漏了什么?

类比 \(\operatorname{CYC}_k\),定义:

\[\operatorname{MSET}_k(\mathcal{A})=(\mathcal{A}^k)/G_{\mathcal{A}} \]

直接用 Burnside 引理:

\[\operatorname{MSET}_k(A(x))=\dfrac{1}{k!} \sum\limits_{g \in G_k} \sum\limits_{i \in cir(g)} A(x^i) \]

其中 \(cir(g)\)\(g\) 分解成循环后各循环大小构成的可重集

当然写成这个样子没办法算,交换和号:

\[\operatorname{MSET}_k(A(x))=\dfrac{1}{k!} \sum\limits_{S} (\sum\limits_{i \in S} A(x^i)) (\sum\limits_{g \in G_k} [cir(g)=S]) \]

那么 \(S\) 的个数只有拆分数级别,很优秀了。

至于 \(\sum\limits_{g \in G_k} [cir(g)=S]\) 怎么求?

\(S=\{s_1,s_2,s_3,\cdots,s_n\}\),再记 \(c_t=\sum\limits_{i=1}^n [s_i=t]。\)

先将循环分配到不同的位置上:\(\dbinom{k}{s_1,s_2,\cdots,s_n}。\)

再对每个循环分配:\(\prod\limits_{i=1}^n (s_i-1)!\)

最后还要对大小相同的循环去重:\(\prod\limits_{i=1}^k \dfrac{1}{c_i!}\)

\[\sum\limits_{g \in G_k} [cir(g)=S] = \dbinom{k}{s_1,s_2,\cdots,s_n} \prod\limits_{i=1}^n \dfrac{(s_i-1)!}{c_i!}= k! \prod\limits_{i=1}^n \dfrac{1}{s_ic_i!} \]

6.4 Powerset(幂集)构造

可能有些偏题,PSET 构造不再是群论去重了,但介于推导方式与 MSET 有些类似还是放在这里。

如果说 MSET 构造是完全背包,那么 PSET 构造就是 01 背包,枚举每一个元素是否存在。

\[\operatorname{PSET}(\mathcal{A})=\prod\limits_{a \in \mathcal{A}} (\mathcal{\{E\}}+\{a\}) \]

生成函数完全一致:

\[\operatorname{PSET}(A(x))=\prod\limits_{a \in \mathcal{A}} (1+x^{|a|})=\prod\limits_{i \geq 0} (1+x^i)^{a_i} \]

也记为 \(\overline{\operatorname{Exp}}(A(x))\),叫“改版 Polya 指数”。

推导方式同样是取对数:

\[\operatorname{PSET}(A(x))=\exp (\sum\limits_{i \geq 0} a_i \ln(1+x^i)) \]

同样化开:

\[\operatorname{PSET}(A(x))=\exp (\sum\limits_{i \geq 0} -a_i \sum\limits_{j \geq 1} \dfrac{(-x^i)^j}{j})=\exp (\sum\limits_{i \geq 0} a_i \sum\limits_{j \geq 1} \dfrac{(-1)^{j-1}x^{ij}}{j}) \]

交换和号:

\[\operatorname{PSET}(A(x))=\exp (\sum\limits_{j \geq 1} \dfrac{(-1)^{j-1}}{j} \sum\limits_{i \geq 0} a_ix^{ij})=\exp (\sum\limits_{j \geq 1} (-1)^{j-1} \dfrac{A(x^j)}{j} ) \]

和 MSET 构造几乎一样,同样可以在 \(O(n \log n)\) 的时间内求出。

6.5 逆变换

考虑 MSET 变换与 PSET 变换的逆变换,外面的 \(\exp\) 可以先取 \(\ln\) 去掉。

\[B(x)=\sum\limits_i a_i\sum\limits_{j \geq 1}\dfrac{x^{ij}}{j} \]

\[B(x)=\sum\limits_i a_i\sum\limits_{j \geq 1} (-1)^{j-1} \dfrac{x^{ij}}{j} \]

这两个变换,如果知道了 \(\{a_i\}\),可以在 \(O(n \log n)\) 内算出 \(\{b_i\}\)

那如果知道 \(\{b_i\}\),可不可以快速求出 \(\{a_i\}\) 呢?

当然可以,方法就是先计算小的 \(a_i\),再减去它对 \(b_i\) 的贡献,一步一步递推。

这个东西其实类似于迪利克雷卷积(乘法卷积),可以直接快进到 狄利克雷生成函数浅谈 - gxy001

  • 例题:P7728 旧神归来(Return of Them)

    新出炉的月赛题。

    对叶子写出生成函数 \(F_0(x)\),一番计算后惊奇地发现 \(A(x)=\operatorname{PSET}^{-1}(F_0(x))\)

7. 应用-无标号树计数

  • \(n\) 个点的无标号有根树数量,不区分子树

  • \(n \leq 2 \times 10^5\)


设生成函数为 \(F(x)\)

首先是无序的,是不是上个普通型生成函数就秒了?

当然……不是。

如果你对比一下第二节那个问题的话,这里不区分子树

也就是说,如果在同一个大小里选出了同样的方案(子树),就会算重。

显然方程就是:

\[F(x)=x\operatorname{MSET} (F(x)) \]

7.1 牛顿迭代做法

又想用牛顿迭代了,可是 exp 里的和式不太能搞的样子。


考虑有 \(F_0(x) \equiv 0 \pmod{x^{\frac{n}{2}}}\),我们要算 \(\exp(\sum\limits_{i \geq 1} \dfrac{F(x^i)}{i} )\)

其实当 \(i \geq 2\) 时,\(F_0(x^i) \equiv 0 \pmod{x^n}\),把它们拎出来。

\[P(x)=x \exp(\sum\limits_{i \geq 2} \dfrac{F(x^i)}{i}) \]

调和级数复杂度,\(P(x)\) 可以在 \(O(n \log n)\) 的时间里暴力计算,可以接受。

接下来就很简单了。

\[F(x)=P(x) \exp(F(x)) \]

因为 exp 是指数函数,把 \(i=1\) 单独提出来。

这是不是可以轻松牛顿迭代了?

时间复杂度 \(O(n \log n)\)

但是 exp 常数大得吓人!

\(2 \times 10^5\) 大概要跑十几秒吧,只能过 40pts。

常数!

void solve(long long *s,long long n){
	long long S[N],A[N],B[N],C[N],P[N];

	S[1]=1;
	long long len;
	for(len=4;len<=(n<<1ll);len<<=1ll){
		long long lim=len<<1ll;
		
		for(long long i=1;i<len;i++) P[i]=0;
		for(long long i=2;i<len;i++)
			for(int t=1;t*i<len;t++)
				P[t*i]=(P[t*i]+S[t]*inv[i]%mod)%mod;
		
		poly::Exp(P,P,len-1);
		
		for(int i=len-1;i>=1;i--) P[i]=P[i-1];
		P[0]=0;
		
		for(long long i=0;i<len;i++) A[i]=P[i];
		
		poly::Exp(C,S,lim);
		NTT::solve(A,A,C,len,len);
		
		for(int i=0;i<lim;i++)
			B[i]=(S[i]+mod-A[i])%mod,C[i]=(mod-A[i])%mod;
		C[0]=(C[0]+1)%mod;
			
		INV::solve(C,C,lim);
		NTT::solve(B,B,C,len,len);
		
		for(int i=0;i<len;i++) S[i]=(S[i]+mod-B[i])%mod;
	}
	for(long long i=0;i<=n;i++) s[i]=S[i];
}

7.2 分治 FFT 做法

\[F(x)=x \exp(\sum\limits_i \dfrac{F(x^i)}{i}) \]

(实际上是一个叫 \(\vartheta\) 算子的东西,作用是让 \(f_i \leftarrow if_i\) ,也就是求导再乘上 \(x\)

这东西看起来很古怪,尝试把它变得好看一点。

先取 \(\ln\),这样 \(\exp\) 就没了。

\[\ln F(x)=\ln x+\sum\limits_{i \geq 1} \dfrac{F(x^i)}{i} \]

再求个导:

\[\dfrac{F'(x)}{F(x)}=\dfrac{1}{x}+\sum\limits_{i \geq 1} \dfrac{(F(x^i))'}{i}=\dfrac{1}{x}+\sum\limits_{i \geq 1} x^{i-1}F'(x^i) \]

通分:

\[xF’(x)=F(x)+\sum\limits_{i \geq 1} F(x) x^iF'(x^i) \]


\(G(x)=\sum\limits_{i \geq 1} x^iF'(x^i)=\sum\limits_{i \geq 1} \sum\limits_{j \geq 1} jf_j x^{i(j-1)}x^i=\sum\limits_{i \geq 1} \sum\limits_{j \geq 1} jf_j x^{ij}\)

容易得到 \(g_n=\sum\limits_{d|n} df_d\),可以 \(O(n \log n)\) 算出 \(F(x)\)

然后由上面那个式子,比较系数:

\[nf_n=f_n+\sum\limits_{i=1}^nf_ig_{n-i} \]

\[f_n=\dfrac{\sum\limits_{i=1}^nf_ig_{n-i}}{n-1} \]

分治 FFT !

边界条件:\(f_1=1\)

时间复杂度 \(O(n \log^2n)\) ,但是由于常数太小跑得飞快~

常数!

void Devide_solve(long long *s,long long l,long long r,long long m){
	if(l==r){
  	long long t=s[l]*l%mod;
		for(int p=l;p<=m;p+=l) G[p]=(G[p]+t)%mod; 
		return ;
	}
	long long mid=(l+r)>>1ll;
	Devide_solve(s,l,mid,m);
	
	long long lim=r-l+1,len=lim>>1ll;
	long long A[N],B[N];
		
	if(l==1){
		for(long long i=1;i<=len;i++) A[i]=s[l+i-1];
		for(long long i=1;i<=len;i++) B[i]=G[i];
		NTT::solve(A,A,B,len,len);
		for(long long i=len+1;i<=lim;i++){
			int t=i-len+mid;
			s[t]=(s[t]+A[i]*inv[t-1]%mod)%mod;
		}
	}
	else{
		
		for(long long i=1;i<=len;i++) A[i]=s[l+i-1];
		for(long long i=1;i<=lim;i++) B[i]=G[i];
		
		NTT::solve(A,A,B,len,lim);
		for(long long i=len+1;i<=lim;i++){
			int t=i-len+mid;
			s[t]=(s[t]+A[i]*inv[t-1]%mod)%mod;
		}
		
		for(long long i=1;i<=len;i++) A[i]=G[l+i-1];
		for(long long i=1;i<=lim;i++) B[i]=s[i];
		
		NTT::solve(A,A,B,len,lim);
		for(long long i=len+1;i<=lim;i++){
			int t=i-len+mid;
			s[t]=(s[t]+A[i]*inv[t-1]%mod)%mod;
		}
	}
	Devide_solve(s,mid+1,r,m);
}

void Devide(long long *s,long long n){
	long long lim=NTT::init(n-1,0);
	long long m=1ll<<lim;
	for(long long i=1;i<=m;i++) s[i]=0;
	s[1]=1;
	Devide_solve(s,1,m,m);
}
  • 例题:有根无标号「奇树」计数

    \[T(x)=\operatorname{MSET}(\operatorname{MSET}(T(x))-1) \]

    \(F(x)=\operatorname{MSET}(T(x))\)\(T(x)=\operatorname{MSET}(F(x)-1)\)

    然后还是这样推式子,分治NTT……

    有个细节,这里 \(f_0=1\),需要在 \(l=r\) 时补上。

7.3 无标号无根树计数

P5900 无标号无根树计数

  • \(n\) 个点的无标号无根树数量,不区分子树

  • \(n \leq 2 \times 10^5\)


再加点料。

已经会无标号有根树计数了,这个应该是小菜一碟

上套路:钦定重心为根,那么根节点的每棵子树大小都应该不超过 \(\lfloor\frac{n}{2}\rfloor\)

直接考虑……还要 Burnside 去重,不好。

不如从反面考虑:有一棵子树超过了 \(\lfloor\frac{n}{2}\rfloor\)

那剩下也可以看成一棵树(根节点就是原来的根节点),并且它的大小绝对不会超过 \(\lfloor\frac{n}{2}\rfloor\) 了,除非这棵子树和剩下的另一棵子树完全相同(即双重心),否则不会算重。

答案就很显然了:

\[ans=f_n-\sum\limits_{k=\lfloor\frac{n}{2}\rfloor+1}^n f_kf_{n-k}-[2|n]C_{f_{\lfloor\frac{n}{2}\rfloor}}^2 \]


int main(){
	NTT::pre();
  inv[1]=1;
	for(long long i=2;i<N;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	
	cin>>n;
	poly::Devide(f,n);
	
	anss=f[n];
	for(long long i=n/2+1;i<=n;i++) anss=(anss+mod-f[i]*f[n-i]%mod)%mod; 
	if(n%2==0) anss=(anss+mod-f[n/2]*(f[n/2]-1)/2%mod)%mod;
	cout<<anss;
}

8. 应用-简单群论与化学计数

广告!群论与Burnside 引理

8.1 烷基计数

LOJ6538 烷基计数 加强版 加强版

  • \(n\) 个碳原子对应的烷基的同分异构体数量。

  • \(n \leq 10^5\)

    • Tips:既求根节点度数不超过 \(3\),且其它节点度数不超过 \(4\) 的无标号有根树数量。

    翻译一下,每个点的儿子数量不超过 \(3\)

    先记生成函数为 \(F(x)\)

    那其实跟无标号有根树计数差不多,其实就是 \(\operatorname{MSET}_3\)

    这里的置换群就是 \(3!=6\) 种置换,直接套用 Burnside 引理。

    分类讨论一下(括号里的数字代表每个循环的大小):

    • \((1,1,1)\)\(F^3(x)\),共有 \(1\) 种。

    • \((1,2)\)\(F(x)F(x^2)\),共有 \(3\) 种。

    • \((3)\)\(F(x^3)\),共有 \(2\) 种。

    于是:

    \[F(x)=x\dfrac{F^3(x)+3F(x)F(x^2)+2F(x^3)}{6} \]

    注意:\(F(0)=1\),因为允许儿子数量小于 \(3。\)

    牛顿迭代直接解方程就好了。

    \(A(x)=F(x^2)\)\(B(x)=F(x^3)\)

    解出来是:

    \[F(x)=F_0(x)-\dfrac{x[F_0^3(x)+3A(x)F_0(x)+2B(x)]-6F_0(x)+6}{x[3F_0^2(x)+3A(x)]-6} \]

    \(O(n \log n)\)

    顺带一提,现在我们可以解决最开始的那个试一试了!

    \(114514\) 个碳原子的烷基共有 \(750576916 \pmod {998244353}\) 种同分异构体。


    void solve(){
    	S[0]=1;
    long long len;
      	for(len=2;len<=(n<<1ll);len<<=1ll){
    	long long lim=len<<1ll;
    		
    		for(long long i=0;i<lim;i++) A[i]=B[i]=P[i]=0,F[i]=S[i];
    		for(long long i=0;i<len/2;i++){
    			A[i*2]=S[i];
    			if(i*3<len) B[i*3]=S[i];
    		}
    		
    		NTT::init(len,0);
    		NTT::ntt(F,lim,1);
    		NTT::ntt(A,lim,1);
    		NTT::ntt(B,lim,1);
    		
    		for(long long i=0;i<lim;i++) P[i]=(F[i]*F[i]%mod+A[i]%mod)*3%mod;
    		NTT::ntt(P,lim,-1);
    		for(long long i=lim-1;i>=1;i--) P[i]=P[i-1];
    		P[0]=mod-6;
    		
    		for(long long i=0;i<lim;i++)
    			Q[i]=(F[i]*F[i]%mod*F[i]%mod+A[i]*F[i]%mod*3+2*B[i])%mod;
    		NTT::ntt(Q,lim,-1);
    		for(long long i=lim-1;i>=1;i--) Q[i]=Q[i-1];
    		Q[0]=0;
    		for(long long i=0;i<lim;i++) Q[i]=(Q[i]+mod-6*S[i]%mod)%mod;
    		Q[0]=(Q[0]+6)%mod;
    		
    		INV::solve(P,P,lim);
    		NTT::solve(F,P,Q,len,len);
    		for(long long i=0;i<len;i++) S[i]=(S[i]+mod-F[i])%mod; 
    	}
    }
    

8.2 烯烃计数

P6597 烯烃计数

  • \(n\) 个碳原子对应的烷烃的同分异构体数量。

  • \(n \leq 10^5\)

    • Tips:即求:一条特殊边连接着两棵树的根节点,这两棵树的根节点度数不超过 \(3\),且其它节点度数不超过 \(4\) 的无向图数量。

    这里要用到烷基计数的 \(F(x)\)

    我们先把每棵树的生成函数 \(P(x)\) 写出来,考虑根节点,还是 Burnside 引理:

  • \((1,1)\)\(F^2(x)\),共有 \(1\) 种。

  • \((2)\)\(F(x^2)\),共有 \(1\) 种。

\[P(x)=x\dfrac{F^2(x)+F(x^2)}{2}-1 \]

\(-1\) 是因为空树不能接在特殊边两边,先处理掉了。

设答案的生成函数为 \(G(x)\)

然后我们把它们接起来,当然还是 Burnside 引理:

\[G(x)=\dfrac{P^2(x)+P(x^2)}{2} \]

\(O(n \log n)。\)


int main(){
	NTT::pre();
  cin>>n;
	solve();
  
	for(long long i=0;i<=n;i++) S2[i*2]=S[i];
	NTT::solve(S,S,S,n,n);
	for(long long i=1;i<=n;i++) p[i]=(S[i-1]+S2[i-1])*inv2%mod;
	
	for(long long i=0;i<=n;i++) p2[i*2]=p[i];
	NTT::solve(q,p,p,n,n);
	for(int i=2;i<=n;i++) q[i]=(q[i]+p2[i])*inv2%mod,cout<<q[i]<<endl;
}

8.3 烷烃计数

P6598 烷烃计数

  • \(n\) 个碳原子对应的烷烃的同分异构体数量。

  • \(n \leq 10^5\)

    • Tips:即求所有节点度数不超过 \(4\) 的无标号无根树数量。

    这里也要用到烷基计数的 \(F(x)\)

    但这次不太一样,是无根树计数!

    还是套用无标号无根树计数的处理方法:钦定根为重心,减掉不合法的方案数。

    但总数并不是 \(f_n\),因为根节点允许四个儿子。

    其实只要考虑根节点就可以了,设所有节点度数不超过 \(4\) 的无标号无根树的生成函数为 \(P(x)。\)

    跟烯烃计数一样,Burnside 引理就可以了。

  • \((1,1,1,1)\)\(F(x)^4\),共有 \(1\) 种。

  • \((1,1,2)\)\(F^2(x)F(x^2)\),共有 \(6\) 种。

  • \((1,3)\)\(F(x)F(x^3)\),共有 \(8\) 种。

  • \((2,2)\)\(F(x^2)^2\),共有 \(3\) 种。

  • \((4)\)\(F(x^4)\),共有 \(6\) 种。

    \[P(x)=x\dfrac{F^4(x)+6F^2(x)F(x^2)+8F(x)F(x^3)+3F^2(x^2)+6F(x^4)}{24} \]


    考虑去重,惊喜地发现:

  • 砍掉的那一棵子树的根节点的儿子个数一定不超过 \(3\)

  • 剩下的子树的根节点(就是原树的根节点)的儿子个数也一定不超过 \(3\)(因为已经砍掉一棵了)。

    然后直接拿 \(F(x)\) 去重即可。

    \[ans=p_n-\sum\limits_{k=\lfloor\frac{n}{2}\rfloor+1}^n f_kf_{n-k}-[2|n]C_{f_{\lfloor\frac{n}{2}\rfloor}}^2 \]

    \(O(n \log n)。\)


int main(){
	NTT::pre();
  cin>>n;
	solve();
  for(long long i=0;i<=n;i++) SS[i]=S2[i*2]=S22[i*2]=S3[i*3]=S4[i*4]=S[i];
	
	NTT::solve(SS,SS,S,n,n);
	NTT::solve(SS,SS,S,n,n);
	NTT::solve(SS,SS,S,n,n);
	
	NTT::solve(S2,S2,S,n,n);
	NTT::solve(S2,S2,S,n,n);
	
	NTT::solve(S22,S22,S22,n,n);
	
	NTT::solve(S3,S3,S,n,n);
	
	anss=(SS[n-1]+S2[n-1]*6%mod+S22[n-1]*3%mod+S3[n-1]*8%mod+S4[n-1]*6%mod)%mod*ksm(24,mod-2)%mod;
	for(long long i=n/2+1;i<n;i++) anss=(anss+mod-S[i]*S[n-i]%mod)%mod; 
	if(n%2==0) anss=(anss+mod-S[n/2]*(S[n/2]-1)/2%mod)%mod;
	cout<<anss;
}

9. 分式域与拉格朗日反演

9.1 分式域与逆元

拉格朗日反演的定理中出现了 \(F^{-n}(x)\) 这种奇怪的东西,不妨先来解释一下。

之前遇到的多项式,\(x\) 的指数都是非负整数。但事实上也可以拓展到整数域,对应多项式就是分式域。

分式域上的加法与卷积仍是一样的,具体实现的时候可以先乘上 \(x^n\)\(-n\)\(x\) 指数的最小值)转化为整式,多项式反转就是一个很好的例子。

拓域后,求逆也得到了拓展,常数项为 \(0\) 的多项式现在可以求逆了。

例如,\(x\) 的逆在整式域上是不存在的,但在分式域上就是 \(x^{-1}\)

具体的方法也很简单:先乘上一个系数 \(x^n\)\(-n\)\(x\) 指数的最小值)转化为常数项非 \(0\) 的整式,求逆后再乘上 \(x^{-n}\) 即可。

例如,求 \((x^2+2x^{-1})^{-1}=[x^{-1}(x^3+2)]^{-1}=x(x^3+2)^{-1}\)

这样一来只要不为 \(0\),分式域的多项式的逆元也有定义了。

9.2 拉格朗日反演

拉格朗日反演用于快速求出多项式复合逆的某一项系数。

复合逆的定义:若 \(F(G(x))=x\)\(G(F(x))=x\) (这两个等式等价),则 \(F(x)\)\(G(x)\) 互为复合逆。

\(F(x)\)\(G(x)\) 的常数项为 \(0\),一次项非 \(0\)

如果已知 \(G(x)\)\(F^k(x)\)\(x^n\) 系数是:

\[[x^n]F^k(x)=\dfrac{k}{n}[x^{-k}]G^{-n}(x) \]

\(k=1\) 时,是更常用的形式:

\[[x^n]F(x)=\dfrac{1}{n}[x^{-1}]G^{-n}(x)=\dfrac{1}{n}[x^{n-1}] (\dfrac{G(x)}{x})^{-n} \]


证明

  • 引理:对于常数项为 \(0\) 而一次项非 \(0\) 的整式 \(F(x)\),有:

\[[x^{-1}]F'(x)F^k(x)=[k=-1] \]

  • 证明:
    • \(k \neq -1\) 时,\(LHS=[x^{-1}] \dfrac{1}{k+1} [F^{k+1}(x)]'\)。而 \([F^{k+1}(x)]'\) 也为整式,故 \(x^{-1}\) 系数为 \(0\)
    • \(k=-1\) 时,\(LHS=[x^{-1}] \dfrac{F'(x)}{F(x)}=[x^0] \dfrac{F'(x)}{F(x)/x}=\dfrac{f_1}{f_1}=1\)

回到原命题,由复合逆定义,有:

\[F(G(x))=x \]

\[F^k(G(x))=x^k \]

两边求导:

\[G'(x) (F^k)'(G(x))=k x^{k-1} \]

\[\sum\limits_i i f^k_i G^{i-1}(x) G'(x) =k x^{k-1} \]

为了凑出 \([x^n]\),两边同除 \(G^n(x)\)

\[\sum\limits_i i f^k_i G^{i-n-1}(x) G'(x) =k x^{k-1} G^{-n}(x) \]

两边取 \(x^{-1}\) 项系数,应用引理:

\[[x^{-1}]\sum\limits_i i f^k_i G^{i-n-1}(x) G'(x) =k [x^{-1}] x^{k-1} G^{-n}(x) \]

\[\sum\limits_i i f^k_i [i-n-1=-1] =k [x^{-k}] G^{-n}(x) \]

\[n f^k_n=k [x^{-k}] G^{-n}(x) \]

即得证。

9.3 拓展拉格朗日反演

  • \(G(F(x))=H(x)\)(请注意这里反过来不是等价的),则:

\[[x^n]G(x)=\dfrac{1}{n}[x^{-1}]H'(x)F(x)^{-n} \]

  • \(F(x)\)\(G(x)\) 互为复合逆,则:

\[[x^n]H(G(x))=\dfrac{1}{n}[x^{-1}]H'(x)F(x)^{-n} \]

这两个形式本质上是一样的。

证明大同小异,略去了。

9.4 应用-求某项系数与解方程

最直接的应用:求 \([x^n] F(x)\)

如果我们知道 \(F(x)\) 的复合逆 \(G(x)\) 的话,就可以由拉格朗日反演快速算出。


  • 例:设 \(n\) 次多项式 \(F(x)\) 满足常数项为 \(0\) 且一次项非 \(0\),对每个 \(k \in [1,n]\) 求出 \([x^n]F^k(x)\)

直接运用拉格朗日反演:

\[[x^n]F^k(x)=\dfrac{k}{n}[x^{-k}]G^{-n}(x)=\dfrac{k}{n} [x^{n-k}] (\dfrac{G(x)}{x})^n \]

所以 cmd 在写什么啊


如何求复合逆?

  • 对任意多项式求复合逆的根号科技,但是我不会而且通常不需要。

  • 若是等式或方程形式,可以考虑将 \(F(x)\) 换成 \(x\)\(x\) 换成 \(G(x)\)

  • 例:\(F(x)=1-\dfrac{1}{e^x}\)

\[x=1-\dfrac{1}{e^{G(x)}} \]

\[e^{G(x)}=\dfrac{1}{1-x} \]

\[G(x)=\ln \dfrac{1}{1-x} \]

  • 例(大朋友与多叉树):\(F(x)=x+\sum\limits_{i \in S} F(x)^i\)

\[x=G(x)+\sum\limits_{i \in S} x^i \]

\[G(x)=x-\sum\limits_{i \in S} x^i \]

10. 指数型生成函数(EGF)

普通型生成函数只能解决无序的计数。

那有序的,自然就要点新科技了。

指数型生成函数最好理解成序列染色。

指数型生成函数其实就是多除了一个 \(i!\)

\[\hat F(x)=\sum\limits_i \dfrac{f_i}{i!}x^i \]

hat 只是一个记号,表示它是指数型生成函数)

10.1 有序计数

  • 现在有 \(2\) 只黄色的蜜蜂,\(5\) 只蓝色的蜜蜂和 \(3\) 只红色的蜜蜂,同种蜜蜂之间不区分区分顺序 ,从中选出 \(7\) 只有多少方法?

先假装我们不知道指数型生成函数是啥。

这次有序了!咋办?

相信第一反应肯定是答案乘个 \(7!\)

这当然不对啊!比如三种颜色的蜜蜂各有 \((1,4,2)\) 只,同种颜色的蜜蜂不区分,自然不对。

实际上,这种情况下的方案应该是 \(\dfrac{7!}{1!4!2!}=105\) (这个叫可重排列,实际上就是在排列的基础上对同种颜色去重)。

发现了吗?完全可以把分母的阶乘放到生成函数里去!

\[\hat A(x)=\sum\limits_i \dfrac{a_i}{i!}x^i \]

对这东西卷积,最后将 \(x^7\) 项系数再乘上 \(7!\) ,就是答案。

10.2 封闭形式

它的封闭形式是什么呢?

非常好看:

\[\hat A(x)=\sum\limits_i \dfrac{a_i}{i!}x^i=e^x \]

很显然的泰勒展开。

第一节里介绍的变换也都是一样的,这里就不重复了。

10.3 练习题

UOJ450【集训队作业2018】复读机

裸的指数型生成函数,拆式子算系数即可。

10.4 点值与下降幂多项式互化

  • 给出多项式 \(F(x)\)\([0,n] \bigcap N\) 处的 \(n+1\) 个点值,求其下降幂形式。

  • \(n \leq 10^5\)


\(a_i=F(i)\)\(A(x)\)\(a_n\) 的生成函数。

\[a_k=\sum\limits_{i=0}^n f_i k^{\underline{i}}=k!\sum\limits_{i=0}^n \dfrac{f_i}{(k-i)!} \]

\[\dfrac{a_k}{k!}=\sum\limits_{i=0}^n \dfrac{f_i}{(k-i)!} \]

\[\sum\limits_{k=0}^n \dfrac{a_k}{k!}=\sum\limits_{k=0}^n \sum\limits_{i=0}^n f_i \dfrac{1}{(k-i)!} \]

\[A(x)=F(x) e^x \]

\[F(x)=A(x)e^{-x} \]

这样就可以 \(O(n\log n)\) 实现点值和下降幂互化。

于是可以实现下降幂多项式乘法。

10.5 exp 的组合意义

  • 现在有很多只互相区分的蜜蜂,但是这次我们要把它们装到一些互不区分的蜂巢里。

  • 蜜蜂们认为,用一个蜂巢装 \(i\) 只蜜蜂有 \(f_i\) 种方法。那用 \(k\) 个蜂巢装 \(n\) 只蜜蜂有多少种方法?

答案出人意料地简洁:

\[S(n,k)=\dfrac{n!}{k!}[x^n]\hat F(x)^k \]

But why?

首先转化成蜂巢有序,最后再除以 \(k!\) 即可。

然后相当于给一个长度为 \(n\) 的序列染上 \(k\) 种颜色,如果一种颜色染了 \(i\) 个格子就要乘上 \(f_i\) 的代价,求代价之和。

这不就是经典问题吗?只不过是乘上了一个代价而已。

于是就是指数型生成函数的 \(k\) 次方。

仔细一想,好像挺显然?

我们来用斯特林数实践一下:

第二类斯特林数(集合),显然 \(f_i=1(i \neq 0)\),于是 \(\hat F(x)=e^x-1\)

\[\left \{ \begin{array}{l} \,n \\ m \end{array}\right \} = \dfrac{n!}{m!}[x^n](e^x-1)^m \]

第一类斯特林数(轮换),\(f_i= (i-1)!(i \neq 0)\),于是 \(\hat F(x)=\ln(1+x)\)

(这里用到了 $$\ln(1-x)=-\sum\limits_{i \geq 1} \dfrac{x^{i}}{i}$$)

\[\left [ \begin{array}{l} \,n \\ m \end{array}\right ] = \dfrac{n!}{m!}[x^n]ln(1+x)^m \]

然后就可以去水斯特林数板子了。


那么,如果不指定 \(k\),随意用多少个蜂巢都无所谓,有多少种方法呢?

这就很简单了:

\[s_k=n![x^n]\sum\limits_{k=0}^{+\infty} \dfrac{\hat F(x)^k}{k!} \]

有点眼熟,这是指数型生成函数的定义式?只不过用多项式代替了变量。

\[s_k=[x^n]e^{\hat F(x)} \]

听说这叫做【exp 的组合意义

P5206 [WC2019]数树 的第三问运用到了这个东西,可以去试试

11. 有标号组合构造

笔者在写这一章时文件丢失了……

11.1 标号

此前介绍的都是无标号的组合类,有标号的组合类需要重新定义。

有标号,即每个组合对象的“基本单位” \(a \in \mathcal{A}\) 都带一个唯一的标号,也即它的标号可以对应成一个有序数组 \((s_1,s_2,\cdots,s_{|a|})\)

定义:组合类的标号数组正好形成一个排列,则称这个组合类是“强标号”的;否则若它的标号数组不连续,则是“弱标号”的。

不交并的定义没有区别,接下来定义笛卡尔积。

为了方便描述,定义一个 离散化 函数 \(\rho((s_1,s_2,\cdots,s_k))=(s'_1,s'_2,\cdots,s'_k)\),其中 \((s'_1,s'_2,\cdots,s'_k)\) 是一个 \(k\) 排列,并且两个数组间元素的相对大小关系是相同的。

考虑两群蜜蜂 \(\mathcal{A}\)\(\mathcal{B}\),它们的笛卡尔积其实就是将两群蜜蜂穿插在一起组成一群蜜蜂,但原是 \(a\) 中的和原是 \(b\) 中的蜜蜂的相对顺序都不变。即:

\[a \otimes b=\{(a',b')|\rho(a')=a,\rho(b')=b\} \]

\[\mathcal A \times \mathcal B=\mathcal C=\{\alpha \otimes \beta|\alpha \in \mathcal A,\beta \in \mathcal B \} \]

现在来考虑生成函数,很容易发现其实就是指数型生成函数的卷积,即可重排。蜂群 \(A\)\(B\) 就是元素对应的颜色。

于是可定义有标号组合类的生成函数:

\[A(x)=\sum\limits_{a \in \mathcal{A}} \dfrac{x^{|a|}}{|a|!} \]

若 $$\mathcal{A \times B=C}$$,则 \(A(x)B(x)=C(x)\)

11.2 Sqeuence(序列)构造

与无标号差不多,直接上定义:

\[\operatorname{SEQ}(\mathcal A)=\mathcal{\{E\}+A+A \times A + A \times A \times A + \cdots} \]

由于笛卡尔积意义不同,Sequence 构造的意义也不一样:将一群蜜蜂复制若干份,再组合起来。

\[\operatorname{SEQ}(A(x))=1+A(x)+A(x)^2+A(x)^3+\cdots=\dfrac{1}{1-A(x)} \]

11.3 Pointing(标记)构造

在组合对象 \(a\) 中任意选一个点作为特殊点(或称为“根”)。

\[\operatorname{PNT}(\mathcal{A})=\{a \times \{\mathcal{E_1,E_2,\cdots,E_{|a|}}\}|a \in \mathcal{A}\} \]

注意这里面是无标号笛卡尔积\(\mathcal{E_k}\) 相当于标记 \(k\) 号点。

生成函数就直接是第 \(x_k\) 项系数乘上 \(k\),即前面提到过的 \(\vartheta\) 算子。

\[\operatorname{PNT}(A(x))=xA'(x)=\vartheta A(x) \]

11.4 Cycle(循环)构造

有标号组合类放到群论上,其实与无标号没有什么区别,只是相当于分拆出的每一个元素都是有标号的。

举个例子,一个组合类包含红蓝两只蜜蜂 \(a,b\)。其序列构造的某个元素就会像是 \((a_2,b_2,a_3,a_1,b_1)\) 之类的,在循环置换下与 \((a_3,a_1,b_1,a_2,b_2)\) 等价。

\(G\) 为所有循环构成的置换群列。

\[\operatorname{CYC}(\mathcal{A})=(SEQ(\mathcal{A})-\{\mathcal{E}\})/G_{\mathcal{A}} \]

先分析容为 \(k\) 的元素,记:

\[\operatorname{CYC}_k(\mathcal{A})=(\mathcal{A}^k)/G_{\mathcal{A}} \]

考虑计算它的生成函数,仍用 Burnside 引理。

但现在卷积是有序的,即各元素间都是区分的,因此组合起来的必须都是同一个才为不动点。

\[\operatorname{CYC}_k(A(x))=\dfrac{A(x)^k}{k} \]

求和得:

\[\operatorname{CYC}(A(x))=\sum\limits_{k \geq 0} \dfrac{A(x)^k}{k}=\ln(\dfrac{1}{1-A(x)}) \]

若带上翻转,除了 \(k=1\) 以外置换个数都会变成两倍,仍是一样的。

11.5 Set(集合)构造

有标号 Set 构造与无标号 Multiset 构造是对应的。

\(G\) 为所有置换构成的置换群列。

\[\operatorname{SET}(\mathcal{A})=\operatorname{SEQ}(\mathcal{A})/G_{\mathcal{A}} \]

同上,组合起来的必须都是同一个才为不动点。

\[\operatorname{SET}_k(A(x))=\dfrac{A(x)^k}{k!} \]

\[\operatorname{SET}(A(x))=\sum\limits_{k \geq 0} \dfrac{A(x)^k}{k!}=\exp A(x) \]

对,这就是 8.5 exp 的组合意义

也即,有标号组合类任意组合得到的是它的 exp,逆变换即为 ln。

使用它可以很轻松地在任意图联通图间转化。

11.6 Substitution(子结构)构造

Substitution 意为“代换”,这个构造即将节点替换为组合对象。

\[\mathcal{A} \circ \mathcal{B}=\sum\limits_{k \geq 0} \mathcal{A}_k \boxtimes \operatorname{SET}_k(\mathcal{B}) \]

其中 \(\mathcal{A} \boxtimes \mathcal{B}=\{(a,b)|a \in \mathcal{A},b \in \mathcal{B}\}\)\(|(a,b)|=|b|\),即 \(a\) 只是用来标记区别的。

那这样子就相当于把 \(\mathcal{B}\) 中的 \(k\) 个元素放进 \(a \in \mathcal{A}\)\(k\) 个节点上,即将每个节点替换为一个组合对象。

所以它的生成函数就是复合:

\[A(x) \circ B(x)=A(B(x)) \]


事实上,这个构造可以导出其它构造来。

  • Sqeuence 构造:大小为 \(k\) 的排列有 \(k!\) 个。

    \[S(x)=\sum\limits_{k \geq 0} \dfrac{k!x^k}{k!}=\dfrac{1}{1-x} \]

    \[\operatorname{SEQ}(A(x))=S(x) \circ A(x)=\dfrac{1}{1-A(x)} \]

  • Cycle 构造:大小为 \(k\) 的环有 \((k-1)!\) 个/

    \[S(x)=\sum\limits_{k \geq 0} \dfrac{(k-1)!x^k}{k!}=\ln{\dfrac{1}{1-x}} \]

    \[\operatorname{SEQ}(A(x))=S(x) \circ A(x)=\ln{\dfrac{1}{1-A(x)}} \]

  • Set 构造:大小为 \(k\) 的集合有 \(1\) 个。

    \[S(x)=\sum\limits_{k \geq 0} \dfrac{x^k}{k!}=\exp x \]

    \[\operatorname{SEQ}(A(x))=S(x) \circ A(x)=\exp A(x) \]

11.7 Boxed(装箱)构造

就是加入/删除一个点。

\[\operatorname{ADD}(A(x))=\{b \mid |b|=|a|+1\} \]

\[\operatorname{DEL}(A(x))=\{b \mid |b|=|a|-1\} \]

注意若 \(a_0>0\),在 \(\operatorname{DEL}(A(x))\)\(a_0\) 会被删掉。

至于生成函数,发现它们的意义正好与积分/求导是对应的

\[\operatorname{ADD}(A(x))=\int A(x) dx \]

\[\operatorname{DEL}(A(x))=A'(x) \]

12. 概率型生成函数(PGF)

让我们先离开解析组合的大坑,来看看生成函数更广泛的应用。

比如说将概率扔进去?

\(F(x)=\sum\limits_{i=0}^{+\infty} P(X=i) x^i\)

其中 \(X\) 是一个随机变量。

12.1 概率、期望、方差与生成函数

拿到这个新奇的东西以后,我们尝试去探索它的性质。

首先有:

\[F(1)=\sum\limits_{i=0}^{+\infty} P(X=i)=1 \]

怎么表示它的期望呢?求个导即可。

\[F'(1)=\sum\limits_{i=1}^{+\infty} iP(X=i)=E(x) \]

最后还有几个我没用过的公式:

\[F^{(k)}(1)=E(X^{\underline{k}}) \]

\[DX=F''(1)+F'(1)-(F'(1))^2 \]

12.2 方程与生成函数

其实概率型生成函数我也没做过几道题,而且好像套路都差不多……

P4548[CTSC2006]歌唱王国

  • 不停地扔一个 \(n\) 面骰子,问第一次扔出长度为 \(m\) 的指定序列 \(s_1,s_2,\cdots,s_m\) 的期望次数。
  • \(n,m \leq 10^5\)

请注意:答案不是 \(m^n\)!!!

普通的思路似乎难以解答,尝试将它放上概率型生成函数、

设投掷的次数为随机变量 \(X、\)

\[F(x)=\sum\limits_{i=0}^{+\infty} P(X=i) x^i \]

\[G(x)=\sum\limits_{i=0}^{+\infty} P(X>i) x^i \]

\([x^i]F(x)\) 就是第 \(i\) 次正好扔到了 \(\{a_i\}\) 的概率,\([x^i]G(x)\) 就是还没扔到的概率、

答案就是 \(E(x)=F'(1)\)

首先,考虑 \(F(x)\)\(G(x)\) 的关系,显然有:

\[g_i=f_{i+1}+g_{i+1} \]

意思就是,在还没结束的序列后面再扔一次,要么结束要么没结束:

  • \[xG(x)+1=F(x)+G(x) \]

其次,还有一个比较隐秘的式子:

  • \[(\dfrac{1}{n}x)^mG(x)=\sum\limits_{i=1}^m [s[1,i] \in border(s[1,m])] (\dfrac{1}{n}x)^{m-i} F(x) \]

这是啥意思呢?

左边的意思是在当前的序列后再扔 \(m\) 次骰子,第 \(i\) 次扔出了 \(s_i\)

右边的意思是考虑它会在哪里结束。

按理说扔完 \(m\) 次以后肯定会结束,为什么会提前结束呢?

只有一种可能:先前还没结束的序列的末尾是 \(s\) 的一段前缀。

也就是说,新加进去的 \(i\)\(s_i\) 既是一段后缀,也是一段前缀。

只有当它是 \(s\) 的 border 时才能计算。


接下来就是解方程了。

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

对这个东西求个导:

\[xG'(x)+G(x)=F'(x)+G'(x) \]

(左边不是链式法则,\(xG(x)\) 看成一个多项式)

我们只想求 \(E(x)=F'(1)\) 的值啊,所以令 \(x=1。\)

\[G'(1)+G(1)=F'(1)+G'(1) \]

\[F'(1)=G(1) \]

好!来看下一个:

\[(\dfrac{1}{n}x)^mG(x)=\sum\limits_{i=1}^m [s[1,i] \in \operatorname{border}(s[1,m])] (\dfrac{1}{n}x)^{m-i} F(x) \]

\[\dfrac{1}{n^m}G(1)=\sum\limits_{i=1}^m [s[1,i] \in \operatorname{border}(s[1,m])] \dfrac{1}{n^{m-i}} F(1) \]

\[E(X)=F'(1)=G(1)=\sum\limits_{i=1}^m [s[1,i] \in \operatorname{border}(s[1,m])] \dfrac{1}{n^i} \]

(别忘了 \(F(1)=1\)

用 kmp 预处理,即可 \(O(m)\) 高效处理。

这差不多就是套路啦,留一道练习题吧(其实是不想写)。

P3706 [SDOI2017]硬币游戏

13. 更多生成函数

准备好,我们要离开多项式了!

如你所见,普通型生成函数中 \(x^i\) 并不是只有这一个形式。如指数型生成函数就是 \(\dfrac{x^i}{i!}。\)

13.1 集合幂级数

\[F=\sum\limits_{S \subseteq U} f_S x^S \]

\(x\) 的指数变成了一个集合……它真的只能当“形式”用了。

一些运算:

\[(f+g)_S=f_S+g_S \]

\[(f * g)_S=\sum\limits_{L \otimes R=S}f_Lg_R \]

\(\otimes\) 是一种定义在集合上的二元运算,比如与、或、异或之类的。

当然这个乘法也叫卷积。

AT4996 [AGC034F] RNG and XOR

P5326 [ZJOI2019]开关

13.2 狄利克雷生成函数

这不是数论里的吗?

\[F(x)=\sum\limits_{i=1}^{+\infty} f_i \dfrac{1}{i^x} \]

那加减法就不说了,卷积是怎么样的呢?

\[F(x)G(x)=\sum\limits_{i=1}^{+\infty} \sum\limits_{i=1}^{+\infty} f_i \dfrac{1}{i^x}g_j \dfrac{1}{j^x}=\sum\limits_{i=1}^{+\infty} \sum\limits_{i=1}^{+\infty} f_i g_j \dfrac{1}{(ij)^x} \]

所以:

\[F(x)G(x)=\sum\limits_{n=1}^{+\infty} \dfrac{1}{n^x} \sum\limits_{d|n} f_dg_{\frac{n}{d}} \]

乘法卷积!

正是熟悉的狄利克雷卷积。

封闭形式牵扯到黎曼函数,这里不展开了,更详细地可以看看这篇:

狄利克雷生成函数浅谈 - gxy001

所以其实这里面也有一套多项式工业,只不过简单得多。

那么前置知识里的经典结论其实也有类似结论:

\[\ln(\dfrac{1}{1-a^{-x}})=\sum\limits_{i \geq 1} \dfrac{1}{ia^{ix}} \]

14. 后记 & 参考资料

生成函数深似海。

肯定还有多得多得多得多的东西的。

留着补


posted @ 2021-01-28 08:56  苹果蓝17  阅读(2336)  评论(2编辑  收藏  举报