斯特林数与斯特林反演

此文章重构于2019.2.26

斯特林数分为第一类斯特林数和第二类斯特林数,其形式与二项式系数很相似。斯特林数在下降幂与通常幂的变换中也有重要作用。

第一类斯特林数

定义

我们将把n个不同元素分成k个非空圆排列的方案数记为[nk],两种方案不同当且仅当存在一个圆排列仅在其中一种方案中出现。所有形如[nk]都叫做第一类斯特林数,在某些地方第一类斯特林数也被记为snk

递推式

考虑如何对第一类斯特林数进行递推。类似二项式系数的递推,我们对于第n个元素是否单独组成一个圆排列进行分类讨论。如果其单独组成一个圆排列,那么这种情况的方案数显然为[n1k1],否则我们将第n个元素插入已有的圆排列中,显然一共存在n1个本质不同的位置,于是这种情况的方案数就为(n1)[n1k]。于是我们可以得到一个形式优美的递推式:

[nk]=[n1k1]+(n1)[n1k]
通项公式

很遗憾,第一类斯特林数不存在比较简洁的通项公式。

生成函数

虽然通项公式并不存在简洁形式,但是其生成函数或许是非常优美的。

我们按行分开考虑,记fn为第n行的生成函数,即fn=i=0n[ni]xi,从递推式中不难发现fn+1fn的关系即为:

fn+1=(x+n)fn

于是我们可以得到优美的生成函数:

fn=i=0n1(x+i)=xn
求法

通过递推式,我们可以在O(nk)的时间内求出[nk],但我们有更快的方法。

考虑从生成函数出发如何快速求出第n行的所有第一类斯特林数。不难发现上式可以简单的通过分治FFTO(nlog2n)的时间内求出。不过考虑到式子的特殊性,我们可以找到更快的方法:

通过上升幂的性质,我们有:

x2k=xk(x+k)k

考虑如何快速求(x+k)k

(x+k)k=i=0k[ki](x+k)i=i=0k[ki](j=0i(ij)xjkij)=j=0kxj(i=jk[ki](ij)kij)=j=0kxjj!(i=jk([ki]i!)kij(ij)!)

不难发现上式其实是一个卷积,于是我们可以在O(nlogn)的时间内从xk倍增到x2k,也可以很快的从x2k推到x2k+1,于是进行与多项式倍增类似的操作,我们可以在O(nlogn)的时间内求出所有第n行的第一类斯特林数。

一些性质

通过生成函数我们可以发现第一类斯特林数实际上可以代表从上升幂到通常幂的变换,即:

xn=i=0n[ni]xi

通过i=0n1(x+i)转化为i=0n1(xi)我们可以将上升幂转化为更常用的下降幂,不难发现此时第一类斯特林数需要带上符号:

xn=i=0n(1)ni[ni]xi

第二类斯特林数

定义

我们将把n个不同元素分成k个非空集合的方案数记为{nk},两种方案不同当且仅当存在一个集合仅在其中一种方案中出现。所有形如{nk}都叫做第二类斯特林数,在某些地方第二类斯特林数也被记为Snk

递推式

考虑如何对第二类斯特林数进行递推。类似二项式系数的递推,我们对于第n个元素是否单独组成一个集合进行分类讨论。如果其单独组成一个集合,那么这种情况的方案数显然为{n1k1},否则我们将第n个元素插入已有的集合中,显然一共存在k个本质不同的位置,于是这种情况的方案数就为k{n1k}。于是我们可以得到一个形式优美的递推式:

{nk}={n1k1}+k{n1k}
通项公式

幸运的是第二类斯特林数存在一个比较简洁的通项公式。

我们考虑将n个元素划分为k不同的非空集合的方案数,容易发现由于元素互不相同且每个集合是非空的,所以集合也是互不相同的,因此方案数就是{nk}k!

再考虑将n个元素划分为k不同的可空集合的方案数,显然是枚举每个元素放入哪个集合,方案数就是kn

考虑这两种方案之间的关系,对于划分为可空集合,我们可以通过枚举有几个集合是非空的从而得到下面这个等式:

kn=i=0k(ki){ni}i!

显然这是一个二项式反演的形式,对其进行二项式反演可以得到:

{nk}=1k!(i=0k(1)ki(ki)in)
生成函数

通过通项公式,我们容易发现其生成函数的形式。考虑对通项公式进行转化:

{nk}=1k!(i=0k(1)ki(ki)in)=i=0k(1)ki(ki)!ini!

那么我们记fn=i0{ni}xign=i0ini!xi,可以得到:

fn=exgn
求法

通过递推式,我们可以在O(nk)的时间内求出{nk},但我们有更快的方法。

直接通过生成函数,我们就得到了O(nlogn)求第n行所有第二类斯特林数的方法。

一些性质

第二类斯特林数与幂和下降幂也有关系,但是并不如第一类斯特林数那么显然。我们尝试用归纳法证明:

xn=i=0n{ni}xi

n=0时显然成立,考虑当n1时成立如何推导n时也成立:

xn=xxn1=i=0n1{n1i}xxi

我们考虑到xxi=ixi+(xi)xi=ixi+xi+1,于是有:

i=0n1{n1i}xxi=i=0n1i{n1i}xi+{n1i}xi+1=i=0n({n1i1}+i{n1i})xi=i=0n{ni}xi

以上可知证毕。

类似的,我们也有关于上升幂的式子:

xn=i=0n(1)ni{ni}xi

斯特林反演

我们已经得到两个形式优美的式子:

xn=i=0n(1)ni[ni]xixn=i=0n{ni}xi

我们可以把两个式子组合一下:

xn=i=0n(1)ni[ni]xi=i=0n(1)ni[ni](j=0i{ij}xj)=j=0nxj(i=jn(1)ni[ni]{ij})

那么可以发现(i=jn(1)ni[ni]{ij})=[n=j],类似的我们也有(i=jn{ni}(1)ij[ij])=[n=j]

于是就可以用来反演了。假设有两个数组f,g满足:

gi=j=0i{ij}fj

那么我们就有:

fi=j=0i(1)ij[ij]gj

原文:

斯特林数分为第一类斯特林数和第二类斯特林数,其形式和二项式系数非常像,都是二元函数。下面逐一介绍。

第一类斯特林数,设两个变量为nk,那么其表示为:

>[nk]

第一类斯特林数还有另一种表示方式,snk(注意s是小写的)。

它的组合意义是,有n个不同的元素,将它们分成k个非空圆排列的方案数。这个定义看上去有点难理解,其实讲的通俗一点,就是你要将n个元素排成k个圈,一个圈如果可以通过旋转(但是不可以翻转)变成另一个圈,那么两个圈就是相同的。并且圈之间是没有顺序的。

下面来推导一下第一类斯特林数的递推式。我们假设前n1个元素已经摆放好了,那么第n个元素要么自成一个新的圈,要么插入到前n1个元素组成的圈中,一共有n1个空位,所以得到:

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

注意判断边界条件,[n0]=[n=0][nn]=1

我们可以发现用递推式来求解第一类斯特林数是O(nk)的,有没有什么更快的方法?答案是肯定的,下面介绍的方法可以在O(nlogn)的时间内求出k=0,1,2,...,n的所有[nk]

我们首先来研究一下第一类斯特林数的性质,我们知道xk次上升幂为:

>xk=i=0k1x+i

我们把x看作多项式中的未知数,将上式理解为多项式,然后我们考虑将其展开:

>xk=xk+...???

好像一下子推不出来了,这个任务还是比较困难的,那我们直接说出结论,尝试来证明它吧:

xk=i=0k[ki]xi

k=0时,上式左右都为1,显然成立。现在来证明如果这个式子对于k1成立,那么对k也成立,首先按照递推式分解第一类斯特林数:

i=0k([k1i1]+(n1)[k1i])xi

拆开来,得到:

>i=0k[k1i1]xi+(k1)i=0k[k1i]xi

去掉那些无意义的第一类斯特林数,再把第一个和式的i枚举范围从1k改成0k1(差不多就是左移一位的意思),得到:

>xi=0k1[k1i]xi+(k1)i=0k1[k1i]xi

于是我们就可以合并了,得到:

>(x+k1)i=0k1[k1i]xi

因为性质已经对k1成立,因此后面的和式就是xk1,于是这个式子就等于xk,得证。

还有一点要说的是,如果把上升幂换成下降幂,很显然每一项只是又乘了(1)ki。其实(1)ki[ki]叫做带符号的第一类斯特林数,也是很有用的,接下来也会提及关于它的应用(如果我不咕咕咕的话)。

直接利用这个性质,我们就可以用分治FFT做到O(nlog2n),这已经很优秀了。不过利用下面的技巧可以做到O(nlogn)

假设我们已经求出了xk的结果,我们可以进行如下操作在O(nlogn)时间内求出(x+k)k

假设我们已经求得:

>xk=i=0kaixi

那么显然有:

>(x+k)k=i=0kai(x+k)i

(x+k)i进行二项式展开:

>(x+k)k=i=0kai(j=0i(ij)xjkij)

再交换一下前后的两个

>(x+k)k=j=0kxj(i=jk(ij)aikij)

已经可以发现一点FFT的影子了,我们再把二项式系数拆开并把j!提到前面:

>(x+k)k=j=0kxj1j!(i=jk(aii!)kij(ij)!)

于是就可以FFT了,接着我们计算x2k=xk(x+k)k,就可以倍增求答案了,如果要计算x2k+1,只要再暴力乘以x+2k即可。于是递归式就是T(n)=T(n2)+O(nlogn),利用主定理计算得T(n)=O(nlogn)

下面来讲一下第一类斯特林数的应用,有一个应用是可以利用带符号的第一类斯特林数在O(k2)时间内求得i=0nik,即自然数幂次和(不过好像有更直观的做法...其实这是一个k+1次的多项式,所以计算k+2个不同的值再暴力插值插出多项式也可以。用高斯消元是O(k3)的,而用拉格朗日插值是O(k2)或者O(klog2k)的,但第一类斯特林数的做法比较优美,并且是不需要求逆元的)。

我们先来证明一个小引理:

>i=0n(ik)=(n+1k+1)

证明非常简单,还是用归纳法。当n=0时左右两边都为[k=0],显然正确。现在来证明如果对于n1成立,那么对于n也成立。把右边的二项式系数拆开得到:

>i=0k(ik)=(nk)+(nk+1)

把等式两边的(nk)都消掉,得到:

>i=0n1=(nk+1)

该式就是我们在n1时已经证明的形式,因此引理得证。

现在我们来用第一类斯特林数求自然数幂次和。由第一类斯特林数的性质得到:

>(nk)k!=n!(nk)!=nk=i=0k(1)ki[ki]ni

因此:

>nk=(nk)k!i=0k1(1)ki[ki]ni

我们设Sk,n=i=0nik,则:

>Sk,n=i=0n((ik)k!j=0k1(1)kj[kj]ij)

把里面的两项拆开,得到:

>Sk,n=i=0n(ik)k!i=0nj=0k1(1)kj[kj]ij

利用我们之前说的引理把第一项转化了并把后面一项的i合并,得到:

>Sk,n=(n+1k+1)k!j=0k1(1)kj[kj]Sj,n

再把第一项稍微进行一下转化,后面一项的j换成i,得到:

>Sk,n=i=nk+1nik+1i=0k1(1)ki[ki]Si,n

注意到前面那项一定是可以整除的,所以不需要求逆元。接下来这要用这个式子O(k2)递推就好了,这里的第一类斯特林数可以直接用递推式O(k2)求。

呼,第一类斯特林数终于告一段落了。现在来讲清真很多的第二类斯特林数。

设两个变量是nk,那么其表示为:

>{nk}

它的组合意义是,有n个不同的元素,将它们划分为k个非空集合的方案数。集合内和集合间都是无序的。

来考虑第二类斯特林数的递推,我们同样假设前n1个元素已经放好了,那么第n个元素要么成为一个新的集合,要么加入到已有的集合中的一个,那么其递推式就是:

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

现在来考虑一下第二类斯特林数的通项公式(没错第二类斯特林数是有简单的通项公式的,和第一类斯特林数相比就是这么清真。)

考虑把n个不同元素分成k个可空集合,并且集合间是有序的方案数,很显然是kn。而这些方案是由划分为i=0,1,2,...,k个非空集合的方案组成的,注意此时我们要求集合间有序,而非空集合之间因为元素互不相同所以显然是两两不同的,因此方案数就是{ni}i!,那么我们就有:

>kn=i=0k(ki){ni}i!

显然这又是一个二项式反演的形式(二项式反演真是好东西),于是我们就得到:

>{nk}k!=i=0k(1)ki(ki)in

因此:

>{nk}=i=0k(1)ki(ki)ink!

但是直接根据通项公式求是O(k)的,我们还是来考虑一下如何在O(nlogn)的时间内求出k=0,1,2,...,n的所有{nk}

我们把通项公式中的二项式系数拆开,于是分母与分子的k!就消掉了,得到:

>{nk}=i=0k(1)ki(ki)!ini!

这是一个非常简单的FFT形式,直接做即可。复杂度O(nlogn)

第二类斯特林数的一个性质就是我们在求通项公式的时候用到的那个,这个稍加推导也可以求自然数幂次和,并且如果支持求逆元操作,那么其复杂度是O(klogk)的,否则其复杂度是O(k2)的,这比第一类斯特林数更加实用。

我们已经知道(为了方便我把上面式子的nk互换了):

>nk=i=0n(ni){ki}i!

注意到i>k时第二类斯特林数的值一定为0,因此枚举范围改为0~k

>nk=i=0k(ni){ki}i!

因此:

>Sk,n=j=0ni=0k(ji){ki}i!

同样,交换两个的顺序:

>Sk,n=i=0k{ki}i!(j=in(ji))

再次利用之前的引理,后面的和式就转化为:

>Sk,n=i=0k{ki}i!(n+1i+1)

最终得到:

>Sk,n=i=0k{ki}j=ni+1n+1ji+1

显然那个连乘是可以递推的,然后就好了。为什么感觉第二类斯特林数什么都比第一类斯特林数简洁。