Typesetting math: 0%

组合数学学习笔记

本文绝大部分内容来自《混凝土数学》

在被多项式爆踩的时候,我偶然发现了《混凝土数学》这本书,然后兴冲冲入手,一看啥都不会,于是就只能在这里带着推推柿子,尝试理解理解,也方便以后复习。

(本文略过了大部分对OI无用的芝士,可以放心食用)

(顺带一提这略掉的东西可能还有点多)

现在开始!

I.下降幂与上升幂

无限微积分的微分算子DD的意义是显式的

Df(x)=limh0f(x+h)f(x)h

无限微积分的积分算子的意义是隐式的

f(x)dx=g(x)Dg(x)=f(x)

有限微积分的差分算子Δ的意义是显式的

Δf(x)=f(x+1)f(x)

有限微积分的求和算子的意义是隐式的

f(x)δx=g(x)Δg(x)=f(x)

有限微积分的定积分的baf(x)的意义是

i[a,b)Zf(x)

注意此处不一定有a<b。与无限微积分类似,必有baf(x)+abf(x)=0


与有限微积分可以配套使用的东西是下降幂

它的定义是

xm_=m1i=0xi

另一种定义是

xm_=x!(xm)!

明显下一种定义更加普适,因为它也可以为指数是0或者负数的下降幂做出定义。但是上一种定义可以应用于实数下降幂

例如

x1_=1x+1

我们将差分算子应用于下降幂

Δ(xm_)=(x+1)m_xm_=(x+1)!(xm+1)!x!(xm)!=(x+1)x!(xm+1)!(xm+1)x!(xm+1)!=m×x!(xm+1)!=mxm1_

同微分算子应用于常规幂一致。

同时将前缀和算子应用于下降幂也能得到优美的性质,即

baxm_δx=xm+1_m+1|ba

需要注意的是,该式在m=1时有特例,即

bax1_δx=1x+1=Hx|ba

其中Hx=xi=11x,即调和级数。

由此可见Hxlnx的离散近似。

(这也证明了为什么ni=1niO(nlogn)级别的,以及为什么有些离散的式子最终会产生调和级数出来)


同有限微积分意义下的不动点ex一样,无限微积分也存在这样的不动点。它是任何满足

Δf(x)=f(x)

的函数。

上式等价于

f(x+1)f(x)=f(x)

f(x+1)=2f(x)

f(x)=2x即可。即,2xex在离散意义下的近似。

而我们同时对于任意的c,都有

Δ(cx)=cx+1cx=(c1)cx

以及

backδx=cbcac1

(即等比数列求和公式)


此外,还有一类上升幂,它的定义是

x¯n=n1i=0x+i

上升幂与下降幂按照如此方式联系在一起

x¯n=(x+n1)n_

上升幂的应用较少,大部分的时候我们使用下降幂。

II.二项式系数的基本应用

定义1.

(nm)=m1i=0nimi=1i

定义2.

(nm)=n!m!(nm)!

定义3(扩展至实数域的定义):

(rk)={rk_k! (k0)0(k<0) (rR,kZ)

事实证明,实数二项式系数在OI中并没有什么用处

所以我们接下来将默认n,mN

吸收恒等式(非常重要,可以用来推式子):

(nm)=nm(n1m1)

它的兄弟们:

m(nm)=n(n1m1)

(nm)(nm)=n(n1m)

杨辉三角形斜方向求和公式:

nk=0(r+kk)=(r+n+1n)

(释义:从杨辉三角形坐标(r,0)的位置开始,向右下方n个位置的数之和,等于结尾位置下方的数的值)

关于上指标求和公式:

ni=0(im)=(n+1m+1)

(释义:杨辉三角形中某一列前n个数的和,等于结尾位置右下角的数)

而它的有限积分形式:

(xm)δx=(xm+1)+C

同时,它的有限微分形式

Δ(xn)=(xn1)

负整数的二项式系数的性质/上指标反转公式:

(rk)=(1)k(kr1k)

(如果你做过多项式学习笔记中XII.差分与前缀和那题的话,就会发现这正是我们打出差分系数的表后的结果——这就是负整数二项式系数的意义,差分系数

行上交错系数求和:

0km(1)k(rk)=(1)m(r1m)

(释义:杨辉三角形中某一行前n个数中偶数位之和减去奇数位之和,等于结尾处上一格内的数)

(这也解释了为什么杨辉三角形中某一行的所有奇位和等于所有偶位和——因为上一行的同一位置的数是0

对于杨辉三角形中一行的前m个数之和没有封闭形式——

但是加上一些奇奇怪怪的东西后它就可计算了。

例如

mk=0(n2k)(nk)=m+12(nm+1)

(释义:杨辉三角形中第n行前m个数乘上它们到本行中点的距离之和,等于终点右方一个位置的值,乘上m+1的一半)

奇怪的斜方向求和公式:

mk=02k(m+kk)=2m

吸收恒等式的一般形式:

(rm)(mk)=(rk)(rkmk)

二项式系数的推广,多项式系数:

(a1+a2++ama1,a2,,am)=(ai)!ai!=mi=1(mj=iajmj=i+1aj)

范德蒙德卷积公式

nk=0(rk)(snk)=(r+sn)

换句话说,

Cr×Cs=Cr+s

其中Cr意为杨辉三角形第r行。


下面我们来看一道二项式系数基本推式子示例。CF660E Different Subsets For All Tuples

首先,空序列的 mn 种可以单独计算。以下只考虑非空序列。

我们考虑枚举一个 i 表示我们当前考虑的子序列长度为 i。再枚举一个 ji 表示这第 i 个数填到了第 j 个位置。

这时,子序列中前 i1 个数就共有 (j1i1) 种位置的分配。

同时,为避免重复计算,我们强制令此 i 个数都是子序列中前一个数后该值的首次出现。这意味着,此 i 个数每个都有 m 种填法,但剩余 ji 个位置就只有 (m1) 种填法了。此处共有 mi(m1)ji 种可能。

然后,i 后面的东西随意填,共 mnj 种。

全部怼一块,得到一个式子

ni=1nj=i(j1i1)mi(m1)jimnj

考虑交换枚举顺序,得到

nj=1mnjji=1(j1i1)mi(m1)ji

后面的式子看上去很熟悉。考虑提出一个 m,并改为枚举 i1

nj=1mnj+1j1i=0(j1i)mi(m1)ji1

看看,看看,后面不就是一个二项式展开的形式吗?还原回去,就得到

nj=1mnj+1(2m1)j1

为让式子美观,改为枚举 j1

n1j=0mnj(2m1)j

到这里就可以直接 O(n) 计算了。

代码戳这儿


这里是一道比较综合的推式子例题:[NOI2019] 斗主地

本题的关键是猜到一个结论:一次函数洗牌后期望仍是一次函数,二次函数洗牌后期望仍是二次函数。考虑证明。

在证明之前,我们要先写出式子来。不妨设 a1A 表示从顶至底第一堆的牌,b1B 表示从顶至底第二堆的牌。

考虑按照题意构造转移式。考虑第二堆的倒数第 j 张牌在洗牌后倒数第 i+j 个位置的情形。此时,第一堆的倒数前 i 张牌均已被洗入牌堆,且顺序与第二堆的前 j1 张牌可以互换。故此种情形的概率为

(i+j1j1)Ai_Bj_ni+j_

第一堆的倒数第 i 张牌洗牌后在第 i+j 个位置的情形也可以类似得到。故若设洗牌后的数组是 c,则有

ck=i+j=nk+1(i+j1j1)Ai_Bj_ni+j_×bBj+1+(i+j1i1)Ai_Bj_ni+j_×aAi+1

唔,看着好乱。

稍微整理一下吧,我们首先决定把式子换成 ci=j 的形式,然后把后面一大坨与 A 有关的提到前面来。

ci=Aj=1aj(i1j1)Aj_Bij_ni_+Bj=1bj(i1j1)Aij_Bj_ni_

一不做二不休,我们将其复原成原序列 {a} 中的情形。

ci=Aj=1aj(i1j1)Aj_(nA)ij_ni_+nAj=1aA+j(i1j1)Aij_(nA)j_ni_

好像也没简单多少……

不过,因为两半的格式类似,考虑从前一半先动手处理。

Aj=1aj(i1j1)Aj_(nA)ij_ni_=Aj=1aj(i1j1)A!(Aj)!(nA)!(nAi+j)!n!(ni)!=Aj=1aj(i1j1)A!(nA)!n!(ni)!(Aj)!(nAi+j)!=Aj=1aj(i1j1)(niAj)(nA)

看上去明显精悍了许多。

发现,对于常规的 {a},化简应该不能继续下去了;但是因为期望具有线性性,即 E(x+y)=Ex+Ey,所以我们可以将 {a} 拆成一些合适的数列的和,若这些数列每一个都满足洗牌后仍是一次/二次函数的限制,则 {a} 也一定满足。

看着那一坨坨的组合数,我们便考虑下降幂:ai=(i1)k_,其中 k 是一个常数。明显,通过取不同的 k 并求和,一切 {a} 都可以被表示。(证明?明显 k=0n1 得到的数列线性无关(零的数量两两不同),故其张成的数列空间等于全集)

那为啥选 (i1)k_ 而不是 ik_ 呢?因为我们看到那个组合数里有个 j1 所以就想把它搞掉。

Aj=1aj(i1j1)(niAj)(nA)=Aj=1(j1)k_(i1j1)(niAj)(nA)=Aj=1k!(j1k)(i1j1)(niAj)(nA)=Aj=1k!(i1k)(ik1jk1)(niAj)(nA)(吸收恒等式)

此时,我们发现可以去掉 j 的限制,因为 j>A 时最后一个二项式系数为 0j<1 时倒数第二个二项式系数为 0。于是将常数提到开头并换成枚举 jk1

Aj=1k!(i1k)(ik1jk1)(niAj)(nA)=k!(i1k)(nA)j(ik1j)(niAk1j)=k!(i1k)(nA)(nk1Ak1)(范德蒙德卷积)=(i1)k_×(nk1Ak1)(nA)

乘号后面的一大坨是与 i 无关的常数,可以看作是 aitai。则,一 k 次下降幂多项式洗牌后变成了次数不大于 k 的多项式,则一堆次数不超过 m 的下降幂多项式之和洗牌后次数也不会大于 m,也即任一多项式洗牌后次数不大于原本次数。证毕。

代码戳这儿


二项式系数的另一基本推论是卡特兰数。其是一经典数列,有着 n 个节点的二叉树、n 个数的入栈序列、长度为 2n 的括号序列等一系列实际意义。但是,一个最经典的模型,却是从点 (0,0) 走到 (2n,0),其中每步可以向右上/右下走一步,但全程不能到 x 轴以下的方案数。

其通项公式是 C(n)=(2nn)(2nn1)。该式子可以用全部方案减去不合法(即到 x 轴以下)的路径来得到。具体而言,全部路径数显然是 (2nn);而不合法路径,我们就在其第一次到达 x 轴以下时,关于直线 y=1 翻转整个后半条路径,这样便得到了终点是 (2n,2) 的全体路径。明显每一条这种路径与全体不合法的路径是一一对应的;而到达点 (2n,2) 的路径数又是 (2nn1),因而得证。

下面我们来看一道应用:CF1204E Natasha, Sasha and the Prefix Sums

这种加一减一前缀和的形式刚好就和我们前面的右上右下移动的模型相同。我们考虑设 f(i) 表示全程不到直线 y=i 以上的方案数。显然其可以直接用我们上述的翻转模型直接得出。而对其进行差分,就得到最大值恰为 i 的方案数。时间复杂度 O(n+m)

代码戳这儿

III.高阶差分与牛顿展开

高阶差分算符Δn的定义:

Δnf(x)=nk=0(nk)(1)nkf(x+k)

它的证明可以通过定义移位算符Ef(x)=f(x+1)以及恒等算符1f(x)=f(x)得出:

Δ=E1

所以

Δn=(E1)n=k(nk)Ek(1)nk

Ek就是位移k位符。

我们设一个d次多项式

f(x)=di=0aixi

它可以被唯一地表示成

f(x)=di=0bixi_

的形式。

依据二项式系数的定义((nm)=nm_m!),我们可以将其转成

f(x)=di=0i!bi(xi)

我们令ci=i!bi,于是就有

f(x)=di=0ci(xi)

从而我们可以用二项式系数的倍数之和表示任意多项式。我们称序列{c}为多项式f牛顿展开


我们又有二项式系数的差分结果为

Δ(xi)=(xi1)

故应用牛顿展开,一个多项式的k阶差分可以很轻松地算出:

Δkf(x)=di=0ci(xik)

x=0时,我们有

Δkf(0)={ck(kd)0(k>d)

所以我们就有ck=Δkf(0),即

f(x)=i=0Δif(0)(xi)

(是不是跟泰勒展开挺像的?没错,这就是泰勒展开的离散近似)


我们回到最初的式子

Δnf(x)=nk=0(nk)(1)nkf(x+k)

代入x=0,有

Δnf(0)=nk=0(nk)(1)nkf(k)

又有

cn=Δnf(0)

所以

nk=0(nk)(1)nkf(k)=cn

我们又有

f(k)=ki=0ci(ki)

所以

nk=0(nk)(1)nkki=0ci(ki)=cn

两边同乘(1)n,就有

nk=0(nk)(1)kki=0ci(ki)=(1)ncn

因为牛顿展开系数必有cn=n!an,所以也可以对原本多项式建立这样的关系

nk=0(nk)(1)kki=0aiki=(1)nn!an

我们再来看牛顿展开式

f(x)=i=0Δif(0)(xi)

尝试把它变成同泰勒展开一致的格式,我们会发现它在位置a处的展开刚好是

f(a+x)=i=0Δif(a)i!xi_

同泰勒展开

f(a+x)=i=0f(i)(a)i!xi

一致。


IV.二项式反演与错排问题

简单介绍可以参见这里的LIII。

详细推理见下。

我们有

nk=0(nk)(1)kki=0ci(ki)=(1)ncn

该式中ki=0ci(ki)一段的意义就是f(k),我们将其代入,得到

nk=0(nk)(1)kf(k)=(1)ncn

我们发现这是一种已知f,求c的好办法。

我们设一个函数g(x)=(1)ncn,则就有

g(n)=nk=0(nk)(1)kf(k)

我们在知道f的情况下很容易求出g,但是如果我们只知道g呢?

我们考虑

nk=0(nk)(1)kg(k)

它等于

nk=0(nk)(1)kkj=0(kj)(1)jf(j)

因为当j>k(kj)=0,无影响,故扩大j的枚举范围

nk=0(nk)(1)knj=0(kj)(1)jf(j)

然后把j移到外面

nj=0f(j)nk=0(nk)(kj)(1)k+j

调用吸收恒等式(rm)(mk)=(rk)(rkmk),我们有

nj=0f(j)nk=0(nj)(njkj)(1)k+j

然后移出来与k无关的项

nj=0f(j)(nj)nk=0(njkj)(1)k+j

改为枚举kj

nj=0f(j)(nj)nk=0(njk)(1)k+2j

(1)k+2j显然可以去掉2j,于是

nj=0f(j)(nj)nk=0(njk)(1)k

后半段是一行中奇偶位的二项式系数之差,它只有在nj=0时才为1,所以

nj=0f(j)(nj)[nj=0]

所以只需要保留n=j时的项即可;所以我们最终得到

g(n)=nk=0(nk)(1)kf(k)nk=0(nk)(1)kg(k)=f(n)

它们的格式是对偶的,故反过来一样推的出来,故最终结果为

g(n)=nk=0(nk)(1)kf(k)f(n)=nk=0(nk)(1)kg(k)

它另外还有一个兄弟,就是

g(n)=nk=0(nk)f(k)f(n)=nk=0(nk)(1)nkg(k)

证明和上面类似。


二项式反演一般也可以从容斥角度理解。这就是式子中组合数以及1的次幂的来历。


二项式反演可以用来解决错排问题。用Dn表示n个人的错排数量。

这里先插一嘴,它有一个递推公式Dn=(n1)(Dn1+Dn2),平常用这个就够了。

则我们有

n!=ni=0(ni)Di

它的含义是枚举所有排列中,恰好有i个失配的位置以及ni个匹配的位置的方案数,并求和。

我们如果取g(n)=n!,f(n)=(1)nDn的话,就有

g(n)=ni=0(ni)(1)if(i)

反演得到

f(n)=ni=0(ni)(1)ig(i)

代入f,g的定义,则有

(1)nDn=ni=0(ni)(1)ii!

到这里实际上已经可以O(n)地求出单个Dn了。但是为了求出全部Dn,我们还得继续优化。

展开二项式系数,得到

(1)nDn=ni=0n!i!(ni)!(1)ii!

抵消掉一些东西

(1)nDn=ni=0n!(ni)!(1)i

换成枚举ni

(1)nDn=ni=0n!i!(1)ni

(1)n移过去

Dn=ni=0n!i!(1)i

显然有(1)i=(1)i

Dn=ni=0n!i!(1)i

最终得到

Dn=n!ni=0(1)ii!

到这里就可以O(n)一遍求出所有Dn了。

这个公式的优势是可以在推式子时直接替代掉Dn


下面我们来看一道例题:[MtOI2018]情侣?给我烧了!

我们考虑如果设n对情侣两两不配对的情况数是Dn的话(注意这个Dn和错排的Dn不同),则恰有k对配对的方案数即为

(nk)nk_2kDnk

(nk)意为从n个位置中选择k个作为配对的位置;nk_是选择k对填入;2k是因为每一对都有两种放法;Dnk是剩下部分的方案。

现在我们考虑求出Dn

我们假设每对情侣都是一男一女(没有歧视性取向独特者的意思),则我们考虑最靠前一排放什么:

  1. 两男/两女

我们共有2n(n1)种方案选出两男/两女填入。则我们考虑它们的伴侣,则又有两种可能:

A. 它们的伴侣坐在了一起:

则此时方案数为(n1)Dn2

B. 它们的伴侣没有坐在一起:

则我们就可以把它们的伴侣撮合成一对以表示它们不能坐在一起(贵圈真乱),方案数为Dn1

  1. 一男一女(不是一对)

我们仍有2n(n1)种方式选出它们;而对于它们的伴侣,仍然像之前那样分析即可。

则分析下来,我们就有如下转移式:

Dn=4n(n1)(Dn1+(n1)Dn2)

O(n)预处理出所有Dn,并代入(nk)nk_2kDnk中求值即可。

本方法也可以通过[MtOI2018]情侣?给我烧了!(加强版)

原版代码戳这儿

加强版代码戳这儿


又是一道例题Tokio Marine & Nichido Fire Insurance Programming Contest 2020E O(rand)

乍一看可能会以为是 FMT 方向的题,但实际上不是,因为 FMT 只能处理一方的限制( AND 为定值或 OR 为定值),并不能同时处理。一个必要的观察是所有可能选择的 x 必定有 S 作为子集,且是 T 的子集。于是我们先舍弃那些不可能选到的 x,然后对那些可能选择的 x,舍弃其中在 S,T 中都选择的位和都未选择的位,这样就将问题转换到:选择一些数,使得它们的 AND0OR 为全集。

这等价于它们在每一位上都不全相同。

我们设 f(x) 表示恰有 x 位不相同的方案数,g(x) 表示至多 x 位不相同的方案数。则显然,有

g(x)=xi=0(xi)f(i)

因为我们要求的是 f(|fullset|),所以考虑二项式反演得到

f(x)=xi=0(1)xi(xi)g(i)

现在考虑如何求解 g。我们设 h(x) 表示使得集合中所有数与 x 进行 AND 操作后均相等的集合数。显然这个可以枚举每个 x,然后开个桶简单求解。然后,我们来考虑其实际意义——集合中所有数,在 x 有值的位置上均相等,在 x 无值的位置上可等可不等,故其所有集合中均至多有 nx 位不相同;则 g(x)=|S|=xh(S)。直接求解即可。

代码戳这儿

V.生成函数

一个序列a0,a1,普通生成函数(OGF)定义为

A(z)=i=0aizi

我们记[zn]A(z)=an,即A(z)n次幂系数。

我们将会发现一些神奇的恒等式,例如

(1+z)n=ni=0(ni)zi

ai=(ni)的生成函数是(1+z)n

我们会发现

ai=(ri)×bi=(si)=(1+z)r×(1+z)s=(1+z)r+s

即范德蒙德卷积。

另外,有两个奇怪的公式:

1(1z)n+1=k=0(n+kn)zk

因为左边可以被看作

(1z)n1

然后就用二项式定理展开得到

i=0(n1i)(z)i

上指标反转得

i=0(1)i(n+ii)(z)i

i=0(n+ii)zi

另一个奇怪的公式是

zn(1z)n+1=i=0(kn)zk

通过前一个公式向右平移n位得到。


OGF具体有什么意义呢?

很明显一个数列的OGF的一般形式是多项式。既然是多项式,那就可以计算卷积。

我们计算f×g,会发现

(fg)i+j=figj

也就是说,我们假如把fi的意义看作是“有fi种方式取出一个i”,则(fg)i的意义就是,有多少种方法从f,g中各取一个数,它们的和为i

这个意义可以被应用于很多场景下,例如背包或者计数。


这里我们来做一道例题:[国家集训队]整数的lqp拆分

我们考虑设fi,j表示整数i拆分成j个数的结果。再设Fi表示斐波那契数列的第i项。

则我们可以得到

fi,j=jk=0fi1,k×Fjk

fi=fi1×F

而我们又有f0=1

故我们最终的结果就是

fi=Fi

然后我们要求的是

G=i=0fi=i=0Fi

套用等比数列求和,就是

1F1F

F0,可以被忽略。故有

G=11F

关于斐波那契数列的生成函数,它可以由递推式直接得出方程F=xF+x2F+1,然后解得

F=11xx2

所以将F代入G的式子,就有

G=1xx212xx2

稍加变化,就变成

G=x12xx2+1

显然后面那个1只与G0的值有关,而我们并不关心G0的值,故我们可以直接忽略那个1,直接观察式子

x12xx2

我们发现它的分母是个二次多项式,于是我们考虑把它化成

a×11cx+b×11dx

的形式,因为后面那两个分式都可以被还原成等比数列的形式。

为了做到这一点,我们考虑将上式的分母因式分解,得到

x(1(12)x)(1(1+2)x)

则我们在上式中就有c=12,d=1+2。现在考虑求出ab

我们有

a(1dx)+b(1cx)=x

a(1(1+2)x)+b(1(12)x)=x

暴力拆开,得到

a(a+2a)x+b(b2b)x=x

因为右边没有常数项,故左边应有a+b=0,即a=b。代入得

(a+2a)x+(a2a)x=x

22ax=x

因此有a=122=24,b=a=24

所以我们最终得到了

(24)×11(12)x+(24)×11(1+2)x

将分式还原成多项式形式,就有

ans=24×(12)n+24×(1+2)n

2mod109+7下存在,为59713600,故直接代入并使用快速幂求值即可。对于极大的n可以通过费马小定理优化。

代码戳这儿


一个序列a0,a1,指数生成函数(EGF)定义为

ˆA(z)=i=0aizii!

它的命名是基于序列1,1,的EGF

i=0zii!=ez

我们考虑计算^fg,有

(^fg)k=i+j=k(ki)ˆfiˆgj

证明可以通过暴力拆开组合数得到。

就是因为有了这个组合数,EGF可以用来解决顺序问题。例如,AAA的EGF与BBBB的EGF,卷起来后得到的,是AAABBBB的字母的所有排列,而它们的OGF卷在一起就只能得到单独的AAABBBB


下面我们来看一题:[TJOI2019]唱、跳、rap和篮球

考虑二项式反演。我们设f(i)表示钦定i个位置出现鸡你太美的方案数,g(i)表示恰好出现i个鸡你太美的方案数。

则我们有

f(i)=j=i(ji)g(j)

于是我们二项式反演得到

g(i)=j=i(1)ji(ji)f(j)

因为我们要求的答案是g(0),所以就有答案等于

g(0)=j=0(1)j(j0)f(j)=j=0(1)jf(j)

我们考虑f(i)

我们将一组鸡你太美共4个人缩成一个人,则共有n3i个人。我们从中选出i个位置再把它展开成一组鸡你太美,则最终就有(n3ii)种不同的钦定方式。

而剩下的部分可以随便填。我们令F(n,a,b,c,d)表示n个位置随便乱填的方案数。则有

F(i)=(n3ii)F(ni,ai,bi,ci,di)

所以代回答案式,就有

i=0(1)i(n3ii)F(ni,ai,bi,ci,di)

现在我们考虑求出F(n,a,b,c,d)。应用我们之前EGF的芝士,我们发现如果令Uk(x)=ki=0xii!的话,它就是

(Uai×Ubi×Uci×Udi)ni

所以我们NTT计算上式即可做到O(n2logn)

但是我们还可以做的更好——我们令一个F=Uai×Ubi,G=Uci×Udi,则我们要求的是(FG)ni。则我们如果从大往小枚举i的话,就会发现它们都是在上一个的基础上增加了一些东西。则我们可以O(n)更新FG,再O(n)暴力计算(FG)ni(只要求出这一位上的值来)。则总复杂度即是O(n2)

代码戳这儿


这边又有一题[HAOI2018]染色

或许你跟我一样,看到这里的“染色”的实质是排列后,就会条件反射地又想套EGF了。但是实际上这题使用EGF并不会很方便。

我们还是先套用二项式反演。我们设fi表示钦定i种颜色出现s次,剩下的随便填的方案数。再设gi表示有且只有i种颜色出现s次的方案数。显然,g可以直接由f二项式反演得到。则我们只需要求出f即可。

我们有

fi=(mi)n!(s!)i(nsi)!(mi)nsi

其中二项式系数的意义是选择i种颜色;那个分数的意义是把未被钦定的其它颜色看作同一种颜色时的方案数(实际上它有个专门名称叫做“多项式系数”);而最后一个幂的意义是考虑未被钦定的颜色的填法。

求出f后,g即可通过拆开组合数并翻转几次进行NTT得出。

代码戳这儿

VI.两类斯特林数

第二类斯特林数{nk}的定义是将n个数分到k个非空集合的方案数。它的左右是集合符号{},较为易记。

它有如下的递推式

{nk}=k{n1k}+{n1k1}

前一半的释义是第n个数随意放入前k个集合的方案数;后一半的释义是第n个数单独分一个集合的方案数。

递推起始条件是

{00}=1

它有如下几条浅显的性质:

  • {nm}m>n时为0;

  • {n0}=[n=0];

  • {n1}=1;

  • {n2}=2n11;

  • {nn}=1


第一类斯特林数[nk]的定义是将n个数分为k轮换的方案数。

其中“轮换”的定义是形如“A对应BB对应CC对应A“这样的东西。

它有如下的递推式

[nk]=(n1)[n1k]+[n1k1]

前一半的释义是第n个数随意插到前n1个数中某一个数后面的方案数;后一半的释义是第n个数单独分一个轮换的方案数。

递推起始条件仍是

[00]=1

它有如下几条浅显的性质:

  • [nm]m>n时为0;

  • [n0]=[n=0];

  • [n1]=(n1)!;

  • [nn]=1

另外,显然有

[nm]{nm}


每一组排列都等价于一组轮换;所以必有

nk=0[nk]=n!

第二类斯特林数与下降幂有很大的联系——准确地说,有公式

xn=nk=0{nk}xk_

该公式可以通过归纳法证出:

显然当n=0时此式成立;

假设n1处上式成立;我们现在要推出n处也成立。

则有

xn1=n1k=0{n1k}xk_

两边同乘x,有

xn=xn1k=0{n1k}xk_

又有

x×xk_=xk+1_+kxk_

则套用得

xn=n1k=0{n1k}xk+1_+n1k=0{n1k}kxk_

在第一个式子中改为枚举k+1,就有

xn=nk=1{n1k1}xk_+n1k=0{n1k}kxk_

因为奇奇怪怪的第一类斯特林数全都为0,所以我们可以扩大枚举范围

xn=nk=0{n1k1}xk_+nk=0{n1k}kxk_

将两者合并,就是

xn=nk=0(k{n1k}+{n1k1})xk_

发现了什么?中间那一大坨就是{nk}

所以我们最终得到

xn=nk=0{nk}xk_


同理我们可以得出将上升幂转成一般幂的公式:

x¯n=nk=0[nk]xk

而上升幂可以直接转成下降幂。

所以我们最终可以得出普通幂转上升幂/下降幂的全套公式:

xn=nk=0{nk}xk_xn=nk=0{nk}(1)nkx¯kx¯n=nk=0[nk]xkxn_=nk=0[nk](1)nkxk

(关于啥时候要加(1)nk,啥时候不加:牢记x¯nxnxn_,所以用大的展开小的的时候就要加上)

这个重要的公式可以应用于例如[省选联考 2020 A 卷] 组合数问题这题。

我们要求

ni=0f(i)xi(ni)

因为下降幂有这样一个优美的等式

ij_(ni)=nj_(njij)

这个可以通过展开二项式系数得到。

所以我们将f(i)=mj=0ajij转换为下降幂形式f(i)=mj=0bjij_

就得到了

ni=0mj=0bjij_xi(ni)

套用上面式子,我们得到

ni=0mj=0bjnj_xi(njij)

然后交换求和顺序,得到

mj=0bjnj_ni=0xi(njij)

显然当i<j时,(njij)无意义。故我们改为枚举ij,就得到

mj=0bjnj_nji=0xi+j(nji)

继续拆分

mj=0bjnj_xjnji=0xi(nji)

我们可以直接在后面一重循环内部套用二项式定理,就得到了

mj=0bjnj_xj(x+1)nj

改变一下符号

mi=0bini_xi(x+1)ni

完成!

于是O(m2)地预处理第二类斯特林数并得出该多项式的下降幂表达,就可以O(m)地求出上式。

Bonus:标准多项式转下降幂多项式可以做到O(mlogm),但这暂时不是我们这里讨论的内容。

代码戳这儿

另外还有一道类似的题是CF1278F Cards,照着上面的结论推就完事了。代码戳这儿


两类斯特林数的联系:

k{nk}[km](1)nk=[m=n]

以及

k[nk]{km}(1)nk=[m=n]

这被称作反转公式

我们甚至可以对负的斯特林数做出定义——

我们依然想要两类斯特林加法原理成立,即

{nk}=k{n1k}+{n1k1}

[nk]=(n1)[n1k]+[n1k1]

则我们最终会发现有

[nm]={mn}

这种奇妙的对偶性。


两类斯特林数行列上的求法:

第二类斯特林数·行

我们有一个公式

m!{nm}=j(mj)(1)mjjn

这个公式的左边的意义是将n个不同的球放入m个不同的盒子且不允许盒子为空的方案数。右边是在用容斥原理来表示相同意义——选出j个盒子,剩下mj个盒子强制空置,然后每个球就有j种不同的放法,总共jn种放法。

我们将组合数拆开,得到

m!{nm}=jm!j!(mj)!(1)mjjn

约一下就得到

{nm}=jjnj!×(1)mj(mj)!

计算卷积即可。

代码戳这儿


第一类斯特林数·行

我们有递推式

x¯n=i[ni]xi

即第n行的第一类斯特林数的生成函数是x¯n

我们现在考虑求出x¯n的标准幂形式。

假设我们已知x¯n的标准幂,我们现在考虑求出x¯2n的标准幂。

则有公式

x¯2n=x¯n×(x+n)¯n

于是我们现在就要求出(x+n)¯n的标准幂形式。

设我们已经求出

x¯n=iaixi

(x+n)代入到上式中,得到

(x+n)¯n=iai(x+n)i

二项式展开,得到

(x+n)¯n=iaijxjnij(ij)

调换求和顺序

(x+n)¯n=jxjiainij(ij)

确定求和范围并重命名变量

(x+n)¯n=ni=0xinj=iajnji(ji)

拆开组合数

(x+n)¯n=ni=0xinj=iajnjij!i!(ji)!

整理得

(x+n)¯n=ni=0xii!nj=iajj!nji(ji)!

我们设ai表示(x+n)¯ni次项系数,得到

ni=0aixi=ni=0xii!nj=iajj!nji(ji)!

显然对应次数的项系数应该相等,故有

ai=nj=iajj!nji(ji)!i!

我们令

bi=aii!

ci=nni(ni)!

计算卷积后,我们会发现

ai=(bc)i+ni!

这样我们就成功求出了(x+n)¯n的标准幂表达。将其与x¯n卷在一起,就得到了x¯2n

但是要倍增地求出斯特林数的话,还需要由x¯n推出x¯n+1;这个可以直接乘上(x+n)得到,直接O(n)手动卷积即可。

则总复杂度O(nlogn)

代码戳这儿


第二类斯特林数·列

我们设

Hm=i=0{im}xi

套用第二类斯特林的递推式

{nk}=k{n1k}+{n1k1}

得到

Hm=i=0(m{i1m}+{i1m1})xi

改为枚举i1,得到

Hm=xi=0(m{im}+{im1})xi

拆开括号,得到

Hm=x(mHm+Hm1)

继续拆,最终得到

Hm=x1xmHm1

我们已知H0=1

所以就有

Hm=mi=1x1ix

Hm=xmmi=11ix

考虑如何求出mi=11ix

提出一个x,得到

xmmi=1x1i

我们会发现它就等于翻转过来的mi=1xi

而它又等于

xm+1_x

所以我们只需要求出xm+1_即可。

而这个可以跟x¯m一样简单倍增得到。

所以总结一下流程:

  1. 计算xm+1_得到a

  2. 计算ax,得到b

  3. 翻转b,得到c

  4. 计算c1,得到d

  5. 计算xmd,得到答案。

代码戳这儿


第一类斯特林数·列

我们这里固定了有m个轮换,所以考虑让轮换间变得有序,这样就最后在答案中除掉一个m!即可。

考虑一个轮换的生成函数是G=i=1(i1)!xi;但是我们这里使用指数生成函数,则它的指数生成函数ˆG=i=1(i1)!xii!=i=1xii

现在我们考虑对两边求导,得到

ˆG=i=1ixi1i=i=1xi1=i=0xi=11x

然后再积分回去,就得到

ˆG=11x=ln11x

而我们最终的答案是ˆGmm!;所以直接多项式快速幂计算

(ln11x)m

即可。

(虽然实际应用时不需要计算ln11x,直接把它当作i=1xii处理即可)

附带指出,本题极度卡常,请务必注意程序常数。

代码戳这儿

VII.欧拉数

欧拉数nm的定义是,n个数的排列中,恰存在m个位置使得pi<pi+1(称为“升高”)的方案数。

欧拉数具有如下性质:

  • mn时,nm=0

显然一个排列最多只能有n1个升高。

  • nm=nnm1(对称性)

显然一个排列有m个升高,等价于它有nm1个降低,而升高和降低的方案数应是相同的。

  • nm=(m+1)n1m+(nm)n1m1

显然一个有m个升高的排列可以由以下两种方式得到:

  1. 在一个有m个升高但长度是n1的排列中,往一个升高中间或者序列开头插入n。共m+1种方案。

则此部分贡献是(m+1)n1m

  1. 在一个有m1个升高且长度是n1的排列中,往一个降低中间或者序列末尾插入n。共((n1)1(m1))+1=(nm)种方案。

则此部分贡献是(nm)n1m1


一个非常非常重要的恒等式(Worpitzky恒等式

(或许它可以被译作“沃皮茨基”恒等式?)

xn=knk(x+kn)

它可以被数学归纳法证明:

显然当n=0时,有

x0=00(x0)=1

n>0时,假设上式在n1处成立,则我们有

xn1=kn1k(x+kn1)

两边同乘x,得到

xn=xkn1k(x+kn1)

我们这边有一个恒等式,即

x(x+kn)=(k+1)(x+kn+1)+(nk)(x+k+1n+1)

证明可以通过展开两边得到,即

x(x+k)!n!(x+kn)!=(k+1)(x+k)!(n+1)!(x+kn1)!+(nk)(x+k+1)!(n+1)!(x+kn)!

两边同乘(n+1)!(x+kn)!,得到

x(n+1)(x+k)!=(k+1)(x+kn)(x+k)!+(nk)(x+k+1)!

两边同除(x+k)!,得到

x(n+1)=(k+1)(x+kn)+(nk)(x+k+1)

全部展开后就会发现全都抵消掉了,得证。

回到上面的式子

xn=xkn1k(x+kn1)

x乘到右边的二项式系数上,套用刚才式子,就有

xn=kn1k[(k+1)(x+kn)+(nk1)(x+k+1n)]

拆开得到

xn=k(k+1)n1k(x+kn)+k(nk1)n1k(x+k+1n)

在后一半中改为枚举k+1,得到

xn=k(k+1)n1k(x+kn)+k(nk)n1k1(x+kn)

成了!只需要把后面的东西并一起就能得到

xn=k((k+1)n1k+(nk)n1k1)(x+kn)

左边一大坨就是欧拉数的递推公式,所以我们最终就得到

xn=knk(x+kn)

证毕。


那么这个东西可以干什么呢?

它可以用来快速求出欧拉数。

我们看到这题:排列计数

题意很简单,求出所有的nk

我们想起来之前有一个式子

Δ(xn)=(xn1)

我们对其作k阶差分,就有

Δk(xn)=(xnk)

于是对于式子

xn=knk(x+kn)

我们两边同时作m阶差分,得到

Δmxn=knk(x+knm)

xnm阶差分,运用高阶差分的定义

Δmxn=k(mk)(1)mk(x+k)n

所以

knk(x+knm)=j(mj)(1)mj(x+j)n

代入x=0,有

knk(knm)=j(mj)(1)mjjn

对于右边一坨,我们套用在第二类斯特林数·行中使用的公式

m!{nm}=j(mj)(1)mjjn

因此就有

knk(knm)=m!{nm}

考虑两边同乘一个znm,得到

znmknk(knm)=znmm!{nm}

我们在两边同时对m求和,就有

mznmknk(knm)=mznmm!{nm}

在左边交换枚举顺序,于是

knkmznm(knm)=mznmm!{nm}

在左边使用二项式定理,得到

knk(z+1)k=mznmm!{nm}

z1代替z,得到

knkzk=m(z1)nmm!{nm}

在右边二项式展开(z1)nm,得到

knkzk=mm!{nm}kzk(1)nmk(nmk)

这样我们就成功地把二项式系数扔到了另一边

我们交换枚举顺序,得到

kzknk=kzkm(1)nmk(nmk)m!{nm}

显然zk对应的系数应该全都相同,故应有

nk=m(1)nmk(nmk)m!{nm}

为美观,交换km并整理得

nm=k{nk}(nkm)(1)nmkk!

这就是欧拉数的通项公式。

为了变成卷积的形式,我们拆开二项式系数

nm=k{nk}(nk)!m!(nkm)!(1)nmkk!

将与各系数有关的项拆分

m!nm=k{nk}(nk)!k!×(1)nmk(nmk)!

所以我们令

fi={ni}(ni)!i!

gi=(1)ni(ni)!

则我们就有

m!nm=kfk×gm+k

所以我们翻转g做卷积,即可得到翻转的m!nm

于是我们在求出第二类斯特林数后,就可以求出欧拉数了。

代码戳这儿

posted @   Troverld  阅读(541)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示