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 多项式技术
知道多项式是啥:
您最好还要知道卷积(多项式乘法)是啥不用会写。
不,您还要知道多项式求逆,多项式 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 展开
其实这个不是重点。
欲知详情,请看: 知乎-怎样更好地理解并记忆泰勒展开式?
然后有个背下来经典的东西:
可以直接泰勒展开,不过也可用别的方法推:
0.3 广义二项式定理
我们都知道喜闻乐见的二项式定理:
但有时候,我们想让这个 \(n\) 变成负数甚至小数咋办?
设 \(f(x)=(1+x)^\alpha\)
我们有 \(f(x)=\sum\limits_{k \geq 0} \dfrac{f^{(k)}(0)}{k!}x^k\)
解释一下求导:
代入:
啊哈,于是我们还能定义拓展的组合数 \(\dbinom{n}{m}=\dfrac{n^{\underline{m}}}{m!}。\)
1. 普通型生成函数(OGF)
生成函数是啥呢?
我们现在有一个序列 \(\{f_i\}\)
把序列扔到多项式的系数上,就能得到一个对应的多项式:
- 注意:\(x\) 只是形式的自变量,并没有实际意义,想让它取啥就取啥。
当然,更严谨一点,\(x\) 的取值必须要使 \(F(x)\) 收敛,不过暂不考虑。
下面来介绍一些变换。
1.1 k 阶前缀和
我们有一个数列 \(\{a_i\}\) 和它的生成函数 \(A(x)\),求出它的前缀和 \(\{b_i\}\) 。
那 \(\{b_i\}\) 的生成函数是啥呢?
那又多了一个新的多项式 \(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}\)。
啊?这不是多项式吗?咋变成一个数了?
其实这叫生成函数的封闭形式。
哪咋变回来呢?
没事,看广义二项式定理!
当然,要求出 \(k\) 阶前缀和也很简单了:
拿 \(f\) 跟 \(A\) 卷一下就行了,\(O(n \log n)。\)
1.2 平移、伸缩与单位根反演
- 平移
向右平移:直接乘个 \(x^k\) 就好了。
向左平移:乘个 \(x^{-k}\),但还要先把多余的项减掉。
套上求导,就会出现神奇的事情:
- 伸缩
多么显然!
就好像是把系数往后伸缩了一样。
- 单位根反演
第二个能用 \(A(-x)\) 是因为 \((-1)^2=1\)。
那什么 \(x\) 满足 \(x^k=1\) 呢?
聪明的同学们一定都想到了单位根。
这玩意叫单位根反演,拿等比数列随便证一下就行了:
1.3 生成函数与数列通项(上)
扔一个简单的递推数列,比如斐波那契数列(\(f_n=f_{n-1}+f_{n-2}(n>1)\),\(f_0=0\),\(f_1=1\) ) ,想求它的通项公式
相信大家都知道矩阵乘法。(然而要讲的不是这个
相信大家都知道特征方程。(然而不详细解释
相信大家都知道,生成函数也可以拿来推这个?
设 \(F(x)\) 为 \(\{f_n\}\) 的生成函数,显然有:
原因也很简单:
\(n=1\) 的时候还要再补个 \(x\) ,是边界条件。
然后把 \(F(x)\) 提到左边:
这个怎么求呢?我们一会儿再说……
先来看一道更加奇妙的问题……
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\) 个数的方案数。
(其实这是第二章的内容)
那当然把它们变成封闭形式:
下面来说说怎么把这个奇奇怪怪的封闭形式还原成多项式。
先把它变得好看一点:
有二次,显然是没办法直接用拓展二项式做的。
不过联想一下初二的数学知识,我们可以把分式裂开成两个一次的。
经过一番轻松的解方程能得到:
这个形式不太好看,给它乘个常数再变一变:
现在拿它当分母,再用初二的数学知识待定系数:
解一解:
这样就能算了:
是不是很神奇?
当然斐波那契数列也是同样的……
1.4 生成函数与数列通项(下)
- 本节参考《具体数学》7.3 解递归式
可能在 OI 中没啥用?
看看,就为了解个 \(G(x)=\dfrac{1-x-x^2}{1-2x-x^2}\) 这么小的东西,我们都花了这么大篇幅……
有没有什么规律或者公式可以直接算呢?
那就不得不提到那个喜闻乐见的特征方程,简单介绍一下:
如果有 \(k\) 阶常系数线性递推数列 \(f_n\) 满足 :
我们可以写一个很好看的方程,即特征方程:
由代数基本定理,该方程在复数域内有且仅有 \(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\) 项!)。
那是因为有:
真好看……
同时我们发现 \(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)\) 因式分解就能得到 \(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 中的问题:
求出了 \(p_1=1+\sqrt2\),\(p_2=1-\sqrt2\),接下来先对 \(Q(x)\) 求个导:
好,套公式!
然后一下子就得到了 1.3 中的答案:
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\) 次多项式。
为什么呢?
啊,好像和数列递推差不多
把每个 \(F_n(t)\) 看成是数列 \(\{g_i\}\) 的第 \(n\) 项,不过这个数列的每一项是个多项式。
然后还是一样写成封闭形式:
解一下方程,\(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})\)
所以答案就是:
时间复杂度 \(O(m\log m)。\)
好像还有进一步的通项公式,这里不展开了。
生成函数是不是很有用呀?
1.5 数列 k 次幂和
-
给定 \(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\)
写出答案的生成函数:
后面看起来非常难处理……
(其实用分治 + NTT 可以直接爆算!复杂度是 \(O(n\log^2n)\))
看到这个分式,有没有想到可以对 ln 求导?
这是个好主意,因为求导可以相加减,非常好算!
啊,上面多了一个 \(a\) ……写一个新的试试:
于是就有:
\(+n\) 是因为常数项没掉了,要补上。
那现在只要求出 \(G(x)\) 就好了:
后面这个东西也没有什么好算的办法……还是得用分治 + 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\) 同阶。
似乎无法因式分解,考虑用生成函数,直接考虑每个变量的次数:
使用分治 + NTT,可在 \(O(n \log^2n)\) 内算出。
发现答案是一堆多项式之积的形式,这是一个很好的形式。
事实上 Matrix-Tree 定理也是这个形式,因此下面许多变式的技巧都可以套到 Matrix-Tree 定理上
2.2 无序背包
随便思考一下,其实就是 \(n\) 种体积为 \(1\) 的物品,第 \(i\) 种物品有 \(a_i\) 种方案(同种物品不同件的方案是区分的),正好填满大小为 \(k\) 的背包的方案数。
拓展 1:每种物品有体积
设第 \(i\) 种物品的体积为 \(V_i\),则:
其实就是 P4389 付公主的背包。
实现方式:取 ln 后用 \(\ln(1-x)\) 的泰勒展开,再分治 + FFT,\(O(n \log^2n)\)。
拓展 2:每种物品数量有限制
设第 \(i\) 种物品的体积为 \(t_i\) 件,则:
实现方式:分子分母可以分别处理,分子还是取 \(\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\) 个(不能重复)乘起来再求和。形式化的:
这个巧妙的意义使它很容易在题目中出现:
-
CF109C Lucky Tree :将条件改为 \(n\) 元组,且任意两点间有关键边,可转化为这个模型。
-
P3909 异或之积:其实是积之和,而且 \(k=3\) (
这还需要生成函数?)。 -
P5326 [ZJOI2019]开关:最后一步的处理就是这个模型(变式)。
-
Open Cup XXI / Gym102978F Find the LCA:也要用到这个模型(变式)。
-
ARC139F Many Xor Optimization Problems:同上(不过是 \(\max\) 形式)。
另外,拓展的韦达定理也可以这样来理解。
接下来我们把重心放在这个模型的变式上:更多限制,或者说更多维度。
变式 1:Min / Max
以 Open Cup XXI / Gym102978F Find the LCA 为例,推到最后要求:
想法也很简单,直接暴力枚举最小值:
看上去很不可做,但实际上也可以分治 + 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]。\)
高维多项式不好处理,但发现只需要计算 \(y\) 的次数为奇数的系数,可以用单位根反演。
令 \(y=-1\) 与 \(y=1\),得到:
\(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\)。
完全一样的,不再解释了。
另:可以拓展至 \(w\) 进制的异或和,对每一种答案做一遍即可,\(O(wn \log^2 n)\)。
2.4 有序背包
我们的背包刚才是一种一种选的,不考虑顺序.
如果考虑顺序,相当于每种方案乘上了一个可重排列。
好像正好和指数型生成函数的乘法对应上了!
同时这也对应着多项式定理。
又是乘法的形式,只不过是 EGF 的。
可以用在 Matrix-Tree 定理上:P5296 [北京省选集训2019]生成树计数。
更好玩的来了:我们完全可以把它套在 01 背包 的模型上!
直接套,得到:
不考虑 \(k\) 的话,即令 \(x=1\),可以通过一系列技巧做到 \(O(\sum a_i \log^2 \sum a_i)\)。
2.5. 和之积
类似二项式定理拓展的东西。
其实就是从 \(n\) 个括号里选若干个 \(b_i\),剩下的用 \(a\) 补齐。
考虑用多项式的乘积来表示,将 \(a\) 单独用一个多项式 \(1+ax+a^2x^2+\cdots\) 表示;\(b_i\) 由于不能重复,用 \(n\) 个多项式 \(1+b_ix\) 表示:
题目:GYM102978D Do Use FFT,非常巧妙地用这个式子化简,从中也可以看到将朴素问题转化为多项式表示的优势。
3. 应用-树上划分类问题
序列划分由于不涉及生成函数不列在此,可见 浅谈划分类问题。
划分类问题,即将序列或图分割成若干部分,求所有划分方式的权值运算结果(和,积或最值等)的问题。
给定一棵树,将树划分为若干联通块。形式化的,定义划分为 \(P=\{V_1,V_2,\cdots,V_k\}\),其中 \(V_i\) 代表某个联通块的点集。
由于无法枚举联通块,需要用类似树形依赖背包的思路来做,并用导数优化。
下面就以划分积和为例,略过划分和。
3.1 联通块积和
- 给定一棵大小为 \(n\) 的树,定义划分 \(P=\{V_1,V_2,\cdots,V_k\}\) 的权值为:
-
求所有划分的权值之和。
-
\(n \leq 10^5\)。
设 \(f_{u,i}\) 表示以 \(u\) 为根的子树中,根所在的联通块大小为 \(i\),不计算根所在联通块贡献的状态。
考虑转移过程,将子树 \(v\) 加入子树 \(u\) 中:
- 边 \((u,v)\) 不连,状态不变并计算 \(v\) 的贡献。
- 边 \((u,v)\) 连,状态改变并不计算贡献。
预处理令 \(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\}\) 的权值为:
-
求所有划分的权值之和。
-
\(n \leq 10^5\),\(m \leq 10\)。
这个问题与朴素的 3.1 相比复杂了很多,不过实际上可以拆开来考虑。
首先后面那一小块其实把 3.1 中的 \(1\) 换成 \(t\) 即可。带权只需要在预处理时考虑即可(\(f_{u,w_u}=1\)),剩下只需要分别维护 \(1 \sim m\) 次导即可。
先处理一下 \(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) \]
由莱布尼茨公式:
于是可以维护 \(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\}\) 的权值为:
-
求所有划分的权值之和。
-
\(n \leq 10^5\)。
这次有两个变量,因此需要多设一维。类似的,设状态为 \(f_{u,i,j}\)。
类似地定义求导 \(\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}\)。
乘法公式稍微麻烦一点,可以先对一维求偏导:
所以还需要维护 \(\dfrac{\partial}{\partial x}F(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\}\) 的权值为:
-
求所有划分的权值之和。
-
\(n \leq 10^5\)。
实际上就是把 T169751 Nephren 加了个点权,所以现在每个联通块的贡献不再相同,需要考虑多记录一维。
下面称 \(T\) 中的点为关键点。
直接考虑 \(f_{u,i,j,t}\) 分别表示根所在联通块的 \(w\) 权值之和为 \(i\),关键点联通块个数为 \(j\),根是否为关键点,不统计根所在联通块的答案。需要注意当两个根都是关键点时,联通块数量要减 \(1\)。
仿照 2.3 改写,为了板式这里就只写函数名了。
仍然维护 \(\dfrac{\partial}{\partial x}\),\(\dfrac{\partial}{\partial y}\) 和 \(\dfrac{\partial}{\partial x\partial y}\)。
已经完备,时间复杂度 \(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\)。
做一次差卷积即可。时间复杂度 \(O(n \log n)\)。
- 二次函数:\(G(x)=ax^2+bx+c\)。
先复合 \(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}\)
做一次差卷积即可。时间复杂度 \(O(n \log n)\)。
- 一次分式:\(G(x)=\dfrac{ax+b}{cx+d}\)。
先复合 \(\dfrac{1}{1+\dfrac{c}{d}x}\),再复合 \(\dfrac{bc-ad}{cd} x+\dfrac{a}{c}\) 即可。时间复杂度 \(O(n \log n)\)。
- 指数函数:\(G(x)=ae^x\)。
记 \(p_j=j! s_j\),写成生成函数:
可以直接分治乘,时间复杂度 \(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\)。
故其转置为后缀和。
- 例子:\(\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))\)。
于是将 \(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)\) 满足:
其中 \(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\)。
有 \(F(G(x))'=G'(x) F'(G(x))\),由莱布尼茨公式可表示 \(F(G(x))^{(t)}\):
可反演用 \(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 形式幂级数的相邻两项的比值为简单分式,可以由一项的系数快速推到下一项的系数,可以以曼哈顿距离的复杂度从一个位置的值转移到另一个位置(例如莫队形式的组合数前缀和)。
- 例题:[CTS2019] 珍珠
可以线性求出形如 \((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-整数为:
当 \(q=1\) 时对左式取极限得 \([n]_1=n\),即一般正整数。
定义 q-阶乘 及 q-组合数 为:
q-binomial 有许多与一般组合数类似的性质,如 q-binomial 均为非负整数,二项式定理,范德蒙德卷积,卢卡斯定理等。这里暂不详细展开。
q-analog 与生成函数
考虑 \(P(x)=\prod\limits_{i=0}^{n} \dfrac{1}{1-q^ix}\)。
这个形式类似 \(i \dbinom{n+i}{n}=(i+n) \dbinom{n+i-1}{n}\),有:
关于正负号,\(P(x)\) 中的系数一定全为正的,而 \(n!_q\) 也为正。
\(P(x)\) 的形式往往在确定向量空间维数时出现,后面会详细展开说明。
类似的,若设 \(P(x)=\prod\limits_{i=0}^{n} (1+q^ix)\),可以得到:
同样类比 \(i \dbinom{n+1}{i}=(n-i+2) \dbinom{n+1}{i-1}\),可得到:
当然这种方法不止于此,涉及到 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\)。
写成生成函数即:
问题:求域为 \(\mathbb{F}_p\) 的 \(n\) 维空间中 \(k\) 维线性空间数量。
考虑上一个问题中每个 \(k\) 维线性空间会被计算多少次,相当于计算秩恰为 \(k\) 的 \(n\) 元 \(k\) 维向量组数量,即:
故答案为:
子空间反演
类比二项式反演,有:
容易发现 \(q=1\) 时即是二项式反演。
下面是证明,只证从左式推右式。
定义 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}\)。
展开得:
故得:
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\) 个,那它的生成函数为:
5.2 笛卡尔积
假如我们有一棵 \(5\) 个节点的树和 \(2\) 只蜜蜂。
我们把它们放在一起,会得到啥呢?
组成了新的元素:带树的蜜蜂,大小为 \(7\) 。
组成了新的元素:带蜜蜂的树,大小也为 \(7\)。
这就叫笛卡尔积,相当于将 树和蜜蜂 组成一个有序对,大小为二者的和。
现在我们有了元素的笛卡尔积了,那集合的笛卡尔积也比较优美:
现在来考虑它的生成函数。
聪明的同学肯定又发现了:这就是卷积!
有个小问题:\(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\)
我们把它们排成一列,相加起来。
得到了啥?
\(\{\mathcal E\}\) 也是一个组合类,不过它里面只有一个元素,叫空集。
这是啥意思呢?
加的意思是或。
也就是说,从 \(\operatorname{SEQ}(\mathcal A)\) 取一个元素出来,可能是一只单独的蜜蜂,也可能是几只蜜蜂聚在一起,也可能是一个大蜂巢(有顺序)。
生成函数也是一样滴:
5.6 Amplification(膨胀)构造
从组合类里任意取出一群蜜蜂,然后把它们复制成 \(k\) 份组合在一起,再放回去。
每个元素本质上没有变化,只是大小变成了 \(k\) 倍。
生成函数就是将 \(x\) 替换成 \(x^k\):
5.7 试试看!
-
求 \(n\) 个点的无标号有根树数量,区分子树(即将两棵子树交换后视为不同)。
-
\(n \leq 10^5\)
记组合类为 \(\mathcal A\),先把根提出来。
等下,子树有多少个?并不知道啊!
啊,刚刚的序列就是解决这个问题的:
\(A(x)=x\operatorname{SEQ}(A(x))=\dfrac{x}{1-A(x)}\)
这东西怎么解呢?
牛顿迭代即可……或者直接解方程?\(O(n \log n)\)。
6. 群论与无标号组合构造
接下来的几章可能较为深入,如果觉得有困难可以先跳过。
(可能可以一直跳到第 \(8\) 章)
6.1 置换群上的组合类
设组合类 \(\mathcal{A}\) 的每一个元素 \(a\) 都可以被表示为另一个组合类 \(\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\) 上,得到轨道:
也就是组合类的组合类了。
它的生成函数的计算只关心个数,可以用 Burnside 引理,接下来会详细介绍。
6.2 Cycle(循环)构造
令 \(G\) 为所有循环构成的置换群列。
(注意这里 \(\mathcal{B}\) 与 \(\mathcal{A}\) 是相同的)
把 \(\mathcal{A}\) 的元素想象成珠子,就是对项链去重。
先分析容为 \(k\) 的元素,记:
考虑计算它的生成函数,用 Burnside 引理。
对于旋转 \(i\) 次的置换,会产生 \(\gcd(i,k)\) 个等价类,每个等价类的 \(k/ \gcd(i,k)\) 个元素必须相同。
然后枚举 \(\gcd(i,k)\):
接下来求 \(CYC(A(x))\):
交换和号:
设 \(k=dt\),
啊哈,后面那个和号正好是 \(\ln(\dfrac{1}{1-x})\) 的泰勒展开。
记 \(F(x)=\ln(\dfrac{1}{1-A(x)})\),显然可以快速算出。
于是就可以在 \(O(n \log n)\) 的时间计算出 \(CYC(A(x))\)。
6.3 Multiset(可重集)构造
也叫 Euler 变换,记作 \(\mathcal{E(A)}\)。
令 \(G\) 为所有置换构成的置换群列。
(注意这里 \(\mathcal{B}\) 与 \(\mathcal{A}\) 也是相同的)
也记为 \(\operatorname{Exp}(A(x))\)
实际上就是各元素不考虑顺序地拼起来。
计算生成函数,直接考虑方案(即组合类内的一个元素)。
比如现在考虑大小为 \(i\) 的一种方案,当然这样的方案有 \(a_i\) 种。
考虑它可以选几次,当然选多少次都行。
这样的方案有 \(a_i\) 种,其实就是全部乘起来:
再考虑上所有的大小:
这个东西没办法快速计算,请出第二定义!
这里写一写多项式推导的证明:
首先看到这个 \(f_i\) 很不好,取个对数。
然后由前置知识,可以化开来:
换个和号,
大功告成!
已经可以在 \(O(n \log n)\) 的时间内求出了。
还有一个拿 Burnside
引理和 Pólya
定理推的,可以去看看这篇:
等一下,是不是漏了什么?
类比 \(\operatorname{CYC}_k\),定义:
直接用 Burnside 引理:
其中 \(cir(g)\) 是 \(g\) 分解成循环后各循环大小构成的可重集。
当然写成这个样子没办法算,交换和号:
那么 \(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!}\)。
6.4 Powerset(幂集)构造
可能有些偏题,PSET 构造不再是群论去重了,但介于推导方式与 MSET 有些类似还是放在这里。
如果说 MSET 构造是完全背包,那么 PSET 构造就是 01 背包,枚举每一个元素是否存在。
生成函数完全一致:
也记为 \(\overline{\operatorname{Exp}}(A(x))\),叫“改版 Polya 指数”。
推导方式同样是取对数:
同样化开:
交换和号:
和 MSET 构造几乎一样,同样可以在 \(O(n \log n)\) 的时间内求出。
6.5 逆变换
考虑 MSET 变换与 PSET 变换的逆变换,外面的 \(\exp\) 可以先取 \(\ln\) 去掉。
这两个变换,如果知道了 \(\{a_i\}\),可以在 \(O(n \log n)\) 内算出 \(\{b_i\}\)。
那如果知道 \(\{b_i\}\),可不可以快速求出 \(\{a_i\}\) 呢?
当然可以,方法就是先计算小的 \(a_i\),再减去它对 \(b_i\) 的贡献,一步一步递推。
这个东西其实类似于迪利克雷卷积(乘法卷积),可以直接快进到 狄利克雷生成函数浅谈 - gxy001。
-
新出炉的月赛题。
对叶子写出生成函数 \(F_0(x)\),一番计算后惊奇地发现 \(A(x)=\operatorname{PSET}^{-1}(F_0(x))\)。
7. 应用-无标号树计数
-
求 \(n\) 个点的无标号有根树数量,不区分子树。
-
\(n \leq 2 \times 10^5\)
设生成函数为 \(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)\) 可以在 \(O(n \log n)\) 的时间里暴力计算,可以接受。
接下来就很简单了。
因为 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 做法
(实际上是一个叫 \(\vartheta\) 算子的东西,作用是让 \(f_i \leftarrow if_i\) ,也就是求导再乘上 \(x\))
这东西看起来很古怪,尝试把它变得好看一点。
先取 \(\ln\),这样 \(\exp\) 就没了。
再求个导:
通分:
设 \(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)\)
然后由上面那个式子,比较系数:
分治 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 无标号无根树计数
-
求 \(n\) 个点的无标号无根树数量,不区分子树。
-
\(n \leq 2 \times 10^5\)
再加点料。
已经会无标号有根树计数了,这个应该是小菜一碟。
上套路:钦定重心为根,那么根节点的每棵子树大小都应该不超过 \(\lfloor\frac{n}{2}\rfloor\)。
直接考虑……还要 Burnside
去重,不好。
不如从反面考虑:有一棵子树超过了 \(\lfloor\frac{n}{2}\rfloor\)。
那剩下也可以看成一棵树(根节点就是原来的根节点),并且它的大小绝对不会超过 \(\lfloor\frac{n}{2}\rfloor\) 了,除非这棵子树和剩下的另一棵子树完全相同(即双重心),否则不会算重。
答案就很显然了:
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. 应用-简单群论与化学计数
8.1 烷基计数
-
求 \(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 烯烃计数
-
求 \(n\) 个碳原子对应的烷烃的同分异构体数量。
-
\(n \leq 10^5\)。
- Tips:即求:一条特殊边连接着两棵树的根节点,这两棵树的根节点度数不超过 \(3\),且其它节点度数不超过 \(4\) 的无向图数量。
这里要用到烷基计数的 \(F(x)\) 。
我们先把每棵树的生成函数 \(P(x)\) 写出来,考虑根节点,还是
Burnside
引理: -
\((1,1)\) :\(F^2(x)\),共有 \(1\) 种。
-
\((2)\) :\(F(x^2)\),共有 \(1\) 种。
\(-1\) 是因为空树不能接在特殊边两边,先处理掉了。
设答案的生成函数为 \(G(x)\)。
然后我们把它们接起来,当然还是 Burnside
引理:
\(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 烷烃计数
-
求 \(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\) 系数是:
当 \(k=1\) 时,是更常用的形式:
证明
- 引理:对于常数项为 \(0\) 而一次项非 \(0\) 的整式 \(F(x)\),有:
- 证明:
- 当 \(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\)。
回到原命题,由复合逆定义,有:
两边求导:
为了凑出 \([x^n]\),两边同除 \(G^n(x)\)。
两边取 \(x^{-1}\) 项系数,应用引理:
即得证。
9.3 拓展拉格朗日反演
- 若 \(G(F(x))=H(x)\)(请注意这里反过来不是等价的),则:
- 若 \(F(x)\) 与 \(G(x)\) 互为复合逆,则:
这两个形式本质上是一样的。
证明大同小异,略去了。
9.4 应用-求某项系数与解方程
最直接的应用:求 \([x^n] F(x)\)。
如果我们知道 \(F(x)\) 的复合逆 \(G(x)\) 的话,就可以由拉格朗日反演快速算出。
- 例:设 \(n\) 次多项式 \(F(x)\) 满足常数项为 \(0\) 且一次项非 \(0\),对每个 \(k \in [1,n]\) 求出 \([x^n]F^k(x)\)。
直接运用拉格朗日反演:
所以 cmd 在写什么啊
如何求复合逆?
-
对任意多项式求复合逆的根号科技,但是
我不会而且通常不需要。 -
若是等式或方程形式,可以考虑将 \(F(x)\) 换成 \(x\),\(x\) 换成 \(G(x)\)。
-
例:\(F(x)=1-\dfrac{1}{e^x}\)
- 例(大朋友与多叉树):\(F(x)=x+\sum\limits_{i \in S} F(x)^i\)
10. 指数型生成函数(EGF)
普通型生成函数只能解决无序的计数。
那有序的,自然就要点新科技了。
指数型生成函数最好理解成序列染色。
指数型生成函数其实就是多除了一个 \(i!\)
(hat
只是一个记号,表示它是指数型生成函数)
10.1 有序计数
- 现在有 \(2\) 只黄色的蜜蜂,\(5\) 只蓝色的蜜蜂和 \(3\) 只红色的蜜蜂,同种蜜蜂之间不区分,区分顺序 ,从中选出 \(7\) 只有多少方法?
先假装我们不知道指数型生成函数是啥。
这次有序了!咋办?
相信第一反应肯定是答案乘个 \(7!\)。
这当然不对啊!比如三种颜色的蜜蜂各有 \((1,4,2)\) 只,同种颜色的蜜蜂不区分,自然不对。
实际上,这种情况下的方案应该是 \(\dfrac{7!}{1!4!2!}=105\) (这个叫可重排列,实际上就是在排列的基础上对同种颜色去重)。
发现了吗?完全可以把分母的阶乘放到生成函数里去!
对这东西卷积,最后将 \(x^7\) 项系数再乘上 \(7!\) ,就是答案。
10.2 封闭形式
它的封闭形式是什么呢?
非常好看:
很显然的泰勒展开。
第一节里介绍的变换也都是一样的,这里就不重复了。
10.3 练习题
裸的指数型生成函数,拆式子算系数即可。
10.4 点值与下降幂多项式互化
-
给出多项式 \(F(x)\) 在 \([0,n] \bigcap N\) 处的 \(n+1\) 个点值,求其下降幂形式。
-
\(n \leq 10^5\)。
设 \(a_i=F(i)\),\(A(x)\) 是 \(a_n\) 的生成函数。
这样就可以 \(O(n\log n)\) 实现点值和下降幂互化。
于是可以实现下降幂多项式乘法。
10.5 exp 的组合意义
-
现在有很多只互相区分的蜜蜂,但是这次我们要把它们装到一些互不区分的蜂巢里。
-
蜜蜂们认为,用一个蜂巢装 \(i\) 只蜜蜂有 \(f_i\) 种方法。那用 \(k\) 个蜂巢装 \(n\) 只蜜蜂有多少种方法?
答案出人意料地简洁:
But why?
首先转化成蜂巢有序,最后再除以 \(k!\) 即可。
然后相当于给一个长度为 \(n\) 的序列染上 \(k\) 种颜色,如果一种颜色染了 \(i\) 个格子就要乘上 \(f_i\) 的代价,求代价之和。
这不就是经典问题吗?只不过是乘上了一个代价而已。
于是就是指数型生成函数的 \(k\) 次方。
仔细一想,好像挺显然?
我们来用斯特林数实践一下:
第二类斯特林数(集合),显然 \(f_i=1(i \neq 0)\),于是 \(\hat F(x)=e^x-1\)。
第一类斯特林数(轮换),\(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}$$)
然后就可以去水斯特林数板子了。
那么,如果不指定 \(k\),随意用多少个蜂巢都无所谓,有多少种方法呢?
这就很简单了:
有点眼熟,这是指数型生成函数的定义式?只不过用多项式代替了变量。
听说这叫做【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\) 与 \(B\) 就是元素对应的颜色。
于是可定义有标号组合类的生成函数:
若 $$\mathcal{A \times B=C}$$,则 \(A(x)B(x)=C(x)\)。
11.2 Sqeuence(序列)构造
与无标号差不多,直接上定义:
由于笛卡尔积意义不同,Sequence 构造的意义也不一样:将一群蜜蜂复制若干份,再组合起来。
11.3 Pointing(标记)构造
在组合对象 \(a\) 中任意选一个点作为特殊点(或称为“根”)。
注意这里面是无标号笛卡尔积,\(\mathcal{E_k}\) 相当于标记 \(k\) 号点。
生成函数就直接是第 \(x_k\) 项系数乘上 \(k\),即前面提到过的 \(\vartheta\) 算子。
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\) 为所有循环构成的置换群列。
先分析容为 \(k\) 的元素,记:
考虑计算它的生成函数,仍用 Burnside 引理。
但现在卷积是有序的,即各元素间都是区分的,因此组合起来的必须都是同一个才为不动点。
求和得:
若带上翻转,除了 \(k=1\) 以外置换个数都会变成两倍,仍是一样的。
11.5 Set(集合)构造
有标号 Set 构造与无标号 Multiset 构造是对应的。
令 \(G\) 为所有置换构成的置换群列。
同上,组合起来的必须都是同一个才为不动点。
对,这就是 8.5 exp 的组合意义。
也即,有标号组合类任意组合得到的是它的 exp,逆变换即为 ln。
使用它可以很轻松地在任意图与联通图间转化。
11.6 Substitution(子结构)构造
Substitution 意为“代换”,这个构造即将节点替换为组合对象。
其中 \(\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\) 个节点上,即将每个节点替换为一个组合对象。
所以它的生成函数就是复合:
事实上,这个构造可以导出其它构造来。
-
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(装箱)构造
就是加入/删除一个点。
注意若 \(a_0>0\),在 \(\operatorname{DEL}(A(x))\) 时 \(a_0\) 会被删掉。
至于生成函数,发现它们的意义正好与积分/求导是对应的
12. 概率型生成函数(PGF)
让我们先离开解析组合的大坑,来看看生成函数更广泛的应用。
比如说将概率扔进去?
\(F(x)=\sum\limits_{i=0}^{+\infty} P(X=i) x^i\)
其中 \(X\) 是一个随机变量。
12.1 概率、期望、方差与生成函数
拿到这个新奇的东西以后,我们尝试去探索它的性质。
首先有:
怎么表示它的期望呢?求个导即可。
最后还有几个我没用过的公式:
12.2 方程与生成函数
其实概率型生成函数我也没做过几道题,而且好像套路都差不多……
- 不停地扔一个 \(n\) 面骰子,问第一次扔出长度为 \(m\) 的指定序列 \(s_1,s_2,\cdots,s_m\) 的期望次数。
- \(n,m \leq 10^5\)、
请注意:答案不是 \(m^n\)!!!
普通的思路似乎难以解答,尝试将它放上概率型生成函数、
设投掷的次数为随机变量 \(X、\)
\([x^i]F(x)\) 就是第 \(i\) 次正好扔到了 \(\{a_i\}\) 的概率,\([x^i]G(x)\) 就是还没扔到的概率、
答案就是 \(E(x)=F'(1)\)、
首先,考虑 \(F(x)\) 与 \(G(x)\) 的关系,显然有:
意思就是,在还没结束的序列后面再扔一次,要么结束要么没结束:
-
\[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)\) 看成一个多项式)
我们只想求 \(E(x)=F'(1)\) 的值啊,所以令 \(x=1。\)
好!来看下一个:
(别忘了 \(F(1)=1\))
用 kmp 预处理,即可 \(O(m)\) 高效处理。
这差不多就是套路啦,留一道练习题吧(其实是不想写)。
13. 更多生成函数
准备好,我们要离开多项式了!
如你所见,普通型生成函数中 \(x^i\) 并不是只有这一个形式。如指数型生成函数就是 \(\dfrac{x^i}{i!}。\)
13.1 集合幂级数
\(x\) 的指数变成了一个集合……它真的只能当“形式”用了。
一些运算:
\(\otimes\) 是一种定义在集合上的二元运算,比如与、或、异或之类的。
当然这个乘法也叫卷积。
13.2 狄利克雷生成函数
这不是数论里的吗?
那加减法就不说了,卷积是怎么样的呢?
所以:
乘法卷积!
正是熟悉的狄利克雷卷积。
封闭形式牵扯到黎曼函数,这里不展开了,更详细地可以看看这篇:
所以其实这里面也有一套多项式工业,只不过简单得多。
那么前置知识里的经典结论其实也有类似结论:
14. 后记 & 参考资料
生成函数深似海。
肯定还有多得多得多得多的东西的。
留着补
- 学长的讲义
- 《具体数学 (Concrete Mathematics)》——Ronald L. Graham, Donald E. Knuth, Oren Patashnik
- 【博客】小学生都能看懂的生成函数入门教程 - 自为风月马前卒
- 题解 P5900 【无标号无根树计数】 - Weng_Weijie
- 用命分析概率型生成函数(PGF)——谈一类具有特殊性质的生成函数在算法竞赛中的应用 - 皎月半洒花Orchid
- 狄利克雷生成函数浅谈 - gxy001
- 多项式计数杂谈 - command_block