本文参考了《具体数学——计算机科学基础》的第六章内容,如有侵犯版权,请联系我,我会马上做出声明或修改。
另外,阅读本文,你可能需要先阅读我的数论学习笔记及我的生成函数学习笔记
本文同步发表于我的 Cnblogs
在信息学的学习中,我们通常会遇到很多特殊的数,科学家们赋予了它们特殊的名字,我们可以通过它们特殊的性质解决很多棘手的问题。
斯特林数 Stirling numbers
斯特林数又有第一类斯特林数和第二类斯特林数,一般我们从第二类开始讲起,因为第二类斯特林数要比第一类更为常用,也更适合入门。
1. 斯特林数的认识
我们定义第二类斯特林数 \(\begin{Bmatrix}n\\k\end{Bmatrix}\) 表示将 \(n\) 个元素划分成 \(k\) 个集合的方案数,也可以记为 \(S(n,k)\)。
那么,我们很容易求出一些边界,\(S(0,0)=1,S(1,0)=S(2,0)=\dots=0,S(i,i)=1\) 等。
我们考虑求出 \(\begin{Bmatrix}n\\k\end{Bmatrix}\) 的递推公式。
我们考虑第 \(n\) 个元素如何分组。它可以自己分成一个新组,也可以加入其他任意一组,所以:
我们便得到了第二类斯特林数的递推公式。
我们类似地定义第一类斯特林数 \(\begin{bmatrix}n\\k\end{bmatrix}\) 表示将 \(n\) 个元素划分成 \(k\) 个环的方案数,也可以记为 \(s(n,k)\)。
根据定义及前面所学,我们知道,对于一个 \(k\) 个元素的集合,能划分成 \((k-1)!\) 个不同的环,所以,我们有以下不等式成立:
对于 \(k>0\) 的情况,\((k-1)!\ge1\),上述不等式成立,对于 \(k=0\) 的情况,会发现 \(\begin{bmatrix}0\\0\end{bmatrix}=1,\begin{bmatrix}i\\0\end{bmatrix}=0\),满足 \(\begin{bmatrix}n\\0\end{bmatrix}=\begin{Bmatrix}n\\0\end{Bmatrix}\),不等式取等号。
我们类似地考虑如何递推出 \(\begin{bmatrix}n\\k\end{bmatrix}\),同样考虑第 \(n\) 个元素分到哪里。它可以独自分成一个新的环,也可以加入到之前任意一个位置。
具体有多少个位置呢,我们可以考虑顺时针下每个元素的左侧都计算一次,这样就能不重不漏的算出,总共有 \(n-1\) 个空位可以放。
所以:
至此,我们对两类斯特林数有了大致的认识,接下来我们就要发觉它们的性质了。
2. 斯特林数的性质
斯特林数具有许多优美的性质
性质 2.0:\(\sum\limits_{k=0}^n\begin{bmatrix}n\\k\end{bmatrix}=n!\),(\(n\ge0\) 且 \(n\) 是整数)
这个性质暴力拆开用归纳法应该也是能证明的,但是这个做法并不优美,我们考虑从组合意义的角度入手。
上述性质等号左边可以看做 \(n\) 个元素划分出任意个环的方案数,右边可以看做一个长度为 \(n\) 的排列。
如果我们证明出两者双射,那么也就证明出了性质 2.0。
对于一个长度为 \(n\) 的排列 \(\pi_1,\pi_2,\dots,\pi_m\),我们构造一个 \(n\) 个点,\(n\) 条边的图,对于每一个 \(1\le i\le n\),让点 \(i\) 向 \(\pi_i\) 连边,这样,最后就会形成若干个环,对应一种划分环的方案。
而对于一个划分环的方案,我们对每一个环钦定一个方向,对于每个元素 \(i\),让 \(\pi_i\) 为它顺时针往下的方案数,最终也会形成一个排列。
至此,我们证明出了 \(n\) 个元素划分出任意个环的方案数和一个长度为 \(n\) 的排列两者双射,性质成立。
性质 2.1:\(x^n=\sum\limits_{k=0}^n\begin{Bmatrix}n\\k\end{Bmatrix}x^{\underline k}\),(\(n\ge0\) 且 \(n\) 是整数)。
这里我们最好需要一个更严谨的定义:
对于 \(n\ge0\) 且 \(k<0\),我们定义 \(\begin{Bmatrix}n\\k\end{Bmatrix}=\begin{bmatrix}n\\k\end{bmatrix}=0\)。
还有就是上面的下降次幂多项式还没有定义,需要补充一下:
我们定义一个 \(m\) 次下降次幂多项式 \(x^{\underline m}=x\times(x-1)\times(x-2)\times\dots\times(x-m+1)\)。
有了这个定义,性质 2.1 就可以用归纳法简单证明。
但是在证明这个性质之前,我们先证明另一个性质热热身:
性质 2.1.1:\(x\cdot x^{\underline k}=x^{\underline {k+1}}+k\cdot x^{\underline k}\),(\(k\ge0\) 且 \(k\) 是整数)。
用归纳法非常好证,因为 \(x^{\underline{k+1}}=(x-k)x^{\underline k}=x\cdot x^{\underline k}-k\cdot x^{\underline k}\),移一下项即可得证。
回到性质 2.1,首先有 \(x^0=x^{\underline 0},x^1=x^{\underline 1},x^2=x^{\underline 2}+x^{\underline 1}\) 等显然成立。
我们假设 \(x^{n-1}=\sum\limits_{k=0}^{n-1}\begin{Bmatrix}n-1\\k\end{Bmatrix}x^{\underline k}\),那么:
这时候用我们热身时候的性质 2.1.1:
然后拆开括号:
左边的 \(k+1\) 有点丑,我们用 \(k\) 替换 \(k+1\)
根据刚才更加严谨的定义,我们可以调整一下求和的上下界,并把它们合并:
发现中间那个就是第二类斯特林数的递推公式,所以:
性质成立。
性质 2.2:\(x^{\overline n}=\sum\limits_{k=0}^n\begin{bmatrix}n\\k\end{bmatrix}x^k\),(\(n\ge0\) 且 \(n\) 是整数)。
我们类似地定义上升次幂多项式:
我们定义一个 \(m\) 次上升次幂多项式 \(x^{\overline m}=x\times(x+1)\times(x+2)\times\dots\times(x+m-1)\)。
具体证明同样可以类似的用归纳法来证,这里留给读者思考吧。其实就是我懒。
性质 2.1 教会了我们如何用下降次幂多项式表示成通常幂,性质 2.2 教会了我们如何用通常幂表示上升次幂多项式,那么如何反过来呢?
发现这个东西和当初我们介绍反演的时候很像。我们是否可以用之前的方法,乘上某一个系数构造出一个类似地恒等式?
性质 2.3:若 \(n\ge0\) 且 \(n\) 是整数,则:
2.3.1:
\[x^n=\sum\limits_{k=0}^n\begin{Bmatrix}n\\k\end{Bmatrix}(-1)^{n-k}x^{\overline k} \]2.3.2:
\[x^{\underline n}=\sum\limits_{k=0}^n\begin{bmatrix}n\\k\end{bmatrix}(-1)^{n-k}x^{k} \]
我们发现上升次幂多项式和下降次幂多项式很像,不过系数的符号不太一样,所以乘上一些 \(-1\) 即可。
也就是说,有 \(x^{\underline n}=(-1)^n(-x)^{\overline n}\) 成立,通过这个可以得证。
因此,我们可以利用这几个性质完成几个幂次之间的转化。
性质 2.4(反转公式):若 \(0\le m\le n\) 且 \(n,m\) 均是整数,则:
2.4.1:
\[\sum\limits_k\begin{bmatrix}n\\k\end{bmatrix}\begin{Bmatrix}k\\m\end{Bmatrix}(-1)^{n-k}=[n=m] \]2.4.2:
\[\sum\limits_k\begin{Bmatrix}n\\k\end{Bmatrix}\begin{bmatrix}k\\m\end{bmatrix}(-1)^{n-k}=[n=m] \]
\(n=m\) 的时候显然成立,其他情况分别把性质 2.1 带入性质 2.3.2,性质 2.2 带入性质 2.3.1 即可得证。
斯特林数的性质还有很多,还有一些是和二项式系数有很大的关系,但是很多性质比较冷门,而且证明过程比较复杂,这里不再赘叙。
有兴趣可以去看《具体数学》的表 6-4,里面列举了很多很详细的斯特林数恒等式,若有时间,我也会去写这些恒等式的详细证明。
另外,根据反转公式,我们也有了斯特林数反演:
性质 2.5:对于两个数论函数 \(f(n),g(n)\):
\(f(n)=\sum_{k=0}^n\begin{Bmatrix}n\\k\end{Bmatrix}g(k)\Longleftrightarrow g(n)=\sum_{k=0}^n\begin{bmatrix}n\\k\end{bmatrix}(-1)^{n-k}f(k)\)。
上述讨论都是在 \(n,m\) 为非负整数的范围,如果我们把斯特林数的范围扩展到 \(n,k\le0\) 的情况,并让它继续满足斯特林数的递推式,会发生什么呢?如果你有兴趣的话,可以写出 \(n,k\in[-5,5]\) 的斯特林数三角形,你就会惊奇的发现:
性质 2.6:若 \(n,k\) 为整数,则:
\[\begin{Bmatrix}n\\k\end{Bmatrix}=\begin{bmatrix}-n\\-k\end{bmatrix} \]
是的,斯特林数同样满足很优美的对称性,对偶性,简直是天生一对!
3 斯特林数的求法
了解完斯特林数的相关性质,如何快速求斯特林数呢?
首先,根据斯特林数的递推式,我们能够轻松在 \(O(n^2)\) 的时间内打表求出两类斯特林数。
如果我们需要更快的时间呢?
问题 3.1(第二类斯特林数·行):快速求出一行第二类斯特林数。
我们可以利用性质 2.1,暴力把 \(x^{\underline n}\) 给算出来,分治 NTT 做到 \(O(n\log^2n)\) 的时间,倍增可以更优。
但是还有更加优美的做法。
第二类斯特林数还有一个性质:
证明的话考虑组合意义,左边相当于把 \(n\) 个元素放进 \(k\) 个互不相同的盒子,允许存在空的盒子;右边相当于枚举多少个盒子非空,然后把 \(n\) 个球划分出 \(i\) 份,每份对应一个盒子,总共有 \(i!\) 种对应方法。
发现这个东西可以二项式反演,按照反演套路,假设 \(f(k)=k^n\),\(g(k)=\begin{Bmatrix}n\\k\end{Bmatrix}k!\),
然后就有:
然后我们直接上公式:
把 \(f(k),g(k)\) 带进去,拆开组合数:
约掉 \(k!\):
发现是个卷积形式,直接上 NTT 即可做到 \(O(n\log n)\)。
signed main(){
scanf("%lld",&n);init(n<<2);
f[0]=0,g[0]=1;int ifac=1;
for(int i=1;i<=n;i++){
ifac=ifac*inv[i]%p;
f[i]=ifac*qpow(i,n)%p;
g[i]=g[i-1]*(p-inv[i])%p;
}
Mul(f,g,n,n+1);
print(f,n);
}
问题 3.2(第一类斯特林数·行):快速求出一行第一类斯特林数。
用性质 2.3.2,与第二类斯特林数·行倍增的做法类似。
我们相当于要求出 \(x^{\overline n}\) 展开后每一项系数,考虑倍增,假设我们已经求出了 \(f^*(x)=x^{\overline m}=\sum\limits_{i=0}^ma_ix^i\),那么有:
接下来的问题,相当于就是已知 \(f(x)\),求 \(f(x+n)\)。
组合数展开二项式:
调换求和顺序:
暴力拆开组合数:
做一个差卷积即可。
写了个递推做法,降低常数:
void S1(int *f,int n){
static int g[N],h[N];
int top=0,st[30],m=1;
while(n)st[++top]=n&1,n>>=1;
--top,n=f[1]=1;
while(top){
cpy(g,f,n+1);px(g,fac,n+1);
rev(g,n);
for(int i=0;i<=n;i++)h[i]=qpow(n,i)*ifac[i]%p;
while(m<=(n<<1))m<<=1;
Mul(g,h,m>>1,m);clr(h,m);rev(g,n);
px(g,ifac,n+1);clr(g+n+1,m-n);
Mul(f,g,m>>1,m);n<<=1;clr(g,m);
if(st[top--])
for(int i=(n|=1);i>=1;i--)f[i]=(f[i-1]+(n-1)*f[i]%p)%p;
clr(f+n+1,m-n);
}
clr(g,n+1),clr(h,n+1);
}
问题 3.3(第二类斯特林数·列):快速求出一列的第二类斯特林数。
提供两种不同的做法:
做法一:EGF+exp。
如果我们考虑把原本无序的 \(k\) 个集合换成有序的 \(k\) 个盒子,那么会使得答案多成上一个 \(k!\),最后除掉就可以了。
对于每个盒子,如果不加限制的放,那么它的 EGF 就是 \(G(x)=\sum\limits_{i\ge0}\dfrac{x^i}{i!}=e^x\),但是盒子不能为空,所以要减去 \(1\),所以 \(G(x)=e^x-1\)。
\(k\) 个盒子的 EGF 就是:\(F(x)=G^k(x)\),直接暴力 pow 即可。
for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%p,ifac[i]=ifac[i-1]*inv[i]%p;
for(int i=1;i<=n;i++)f[i]=ifac[i];
powp(f,n,k);
for(int i=0;i<=n;i++)printf("%lld ",f[i]*fac[i]%p*ifac[k]%p);
做法二:倍增 NTT。
我们假设第 \(k\) 列的答案多项式为 \(F_k(x)\),那么:
把递推式按上去:
拆开:
移项:
边界:\(F_0(x)=1\),所以我们可以得出:
前面的一切好说,我们只需要能够快速求出 \(\prod\limits_{i=1}^k(1-ix)\) 即可。
我们对这个式子动一些手脚:
我们在学多项式带余除法时用过一个翻转操作,就是把 \(x\) 代替成 \(x^{-1}\),\((x^{-1}-i)\) 其实就是 \((x-i)\) 的翻转。
那么,我们现在要求的就是 \(\prod\limits_{i=1}^k(x-i)=\dfrac{x^{\underline{k+1}}}{x}\),发现是老朋友了,上面的同款倍增方法再来一遍。
两个方法时间复杂度都是 \(O(n\log n)\),但下面倍增的常数更小。
因为我太懒比较喜欢做法一,所以我选择大常数多项式求幂。
问题 3.4(第一类斯特林数·列):快速求出一列第一类斯特林数。
同样不考虑 \(k\) 个环,假设它们互不相同,最后求出答案再除掉 \(k!\)。
只有一个圆排列的 EGF 显然就是 \(F(x)=\sum\limits_{i\ge0}(i-1)!\dfrac{x^i}{i!}=\ln(1+x)\),那么 \(k\) 个圆排列的 EGF 就是 \((\ln(1+x))^k\),\(\ln\) 部分写开来,直接上多项式 pow。
for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%p,ifac[i]=ifac[i-1]*inv[i]%p;
for(int i=1;i<=n;i++)f[i]=inv[i];
powp(f,n,k);
for(int i=0;i<=n;i++)printf("%lld ",f[i]*fac[i]%p*ifac[k]%p);
呼,深吸一口气,看到这里,你终于学会了一些斯特林数的基本操作了。
4. 附加的斯特林数恒等式及其证明
这些式子都是在《具体数学》中找到的,因为书中没有详细证明,所有有了这个部分。
这一部分全凭我自己乱口胡,如有不严谨或错误的地方,还请指出,谢谢!
以下式子中,均有条件整数 \(n,m,l\ge0\)。
恒等式 4.1:\(\begin{Bmatrix}n+1\\m+1\end{Bmatrix}=\sum\limits_{k=m}^n\dbinom nk\begin{Bmatrix}k\\m\end{Bmatrix}\)。
这个式子也可以这样写:
考虑其组合意义,把 \(n+1\) 个元素分成 \(m+1\) 组,相当于先从 \(n\) 个元素中,选出 \(n-k\) 个和第 \(n+1\) 个元素分成一组,然后再将剩下的 \(k\) 个元素分成 \(m\) 组。
恒等式 4.2:\(\begin{bmatrix}n+1\\m+1\end{bmatrix}=\sum\limits_{k=m}^n\begin{bmatrix}n\\k\end{bmatrix}\dbinom km\)。
右边部分相当于先把前 \(n\) 个元素分成 \(k\) 个圆,然后在其中保留 \(m\) 个,剩下的 \(k-m\) 个圆和第 \(n+1\) 个元素合并成一个圆。
下来的问题相当于有 \(t\) 个圆 \(r_1,r_2,\dots,r_t\) 和元素 \(n\),其中 \(n\) 比其他所有圆中的所有元素都大(注意这个点),如何把它们合并成一个圆。
如何合并呢?我们把每个圆里面最小的元素放到最后一项,按照最小元素的大小从小到大排序,全部接到一起,再把元素 \(n+1\) 接到最后即可。
举个例子,假设我们现在有 \(3\) 个圆 \(\{1,4,7\},\{2,5,3\},\{6,9\}\) 和一个元素 \(10\),那么,我们把它们排完序之后,就会变成:\(\{4,7,1\},\{5,3,2\},\{9,6\}\),然后按顺序接好,那么最后的圆就是 \(\{4,7,1,5,3,2,9,6,10\}\)。
但这也仅仅证明了若干个圆加上一个最大元素能够与一个元素个数与之相同的圆对应,并不能证明两者双射。
那么,我们考虑还原这个过程,也就是分裂一个圆。
首先,我们找到这个圆中最大元素,删掉,并以它往右开始断环成链。
接着,我们找到剩下的元素中的最小值,最小值前面的元素按顺序分成一个圆,并把这些元素从链中删去;接着再找剩下元素中的最小值,分园,删除……以此循环,直至链中没有元素。
这样,我们就成功证明出若干个圆与一个最大元素和一个大圆双射。
恒等式 4.3:\(\begin{Bmatrix}n\\m\end{Bmatrix}=\sum\limits_{k}\dbinom nk\begin{Bmatrix}k+1\\m+1\end{Bmatrix}(-1)^{n-k}\)。
\(n\) 个元素划分成 \(k\) 个集合,我们先选择 \(n-k\) 个元素捆绑成一个元素,然后再继续划分。但是有可能会计重,比如一个大小为 \(i\) 的集合,在 \(k=n\) 的时候计算了 \(i\) 次,在 \(k=n-1\) 的时候计算了 \(i-1\) 次等,所以要乘上一个容斥系数 \((-1)^{n-k}\),其中 \(n-k\) 也就是捆绑集合大小。
恒等式 4.4:\(\begin{bmatrix}n\\m\end{bmatrix}=\sum\limits_{k}\begin{bmatrix}n+1\\k+1\end{bmatrix}\dbinom km(-1)^{n-k}\)。
我们构造一个虚拟元素 \(n+1\),先划分成 \(k+1\) 个圆,其中有一个圆含有虚拟元素,在剩下的圆中保留 \(m\) 个,剩下的用 4.2 的方法合并圆,最后再把虚拟点删掉,同样也要乘上一个容斥系数。
恒等式 4.5:\(\begin{Bmatrix}n\\m\end{Bmatrix}m!=\sum\limits_{k}\dbinom mkk^n(-1)^{m-k}\)。
这个在解决问题 3.1 时证明过,这里不再重复。
恒等式 4.6:\(\begin{Bmatrix}n+1\\m+1\end{Bmatrix}=\sum\limits_{k=0}^n\begin{Bmatrix}k\\m\end{Bmatrix}(m+1)^{n-k}\)
先把前 \(k\) 个元素分成 \(m\) 个集合,第 \(k+1\) 个元素放在第 \(m+1\) 个集合,剩下的 \(n-k\) 个元素无压力乱选。
为什么不会算重呢?对于一种合法方案,假设 \(t\) 时第 \(m+1\) 个集合中编号最小的元素,那么这种方案只会在 \(k=t-1\) 的时候被统计一次。
恒等式 4.7:\(\begin{bmatrix}n+1\\m+1\end{bmatrix}=\sum\limits_{k=0}^n\begin{bmatrix}k\\m\end{bmatrix}n^{\underline{n-k}}=n!\sum\limits_{k=0}^n\dfrac{\begin{bmatrix}k\\m\end{bmatrix}}{k!}\)。
求和后面的相当于 \(\sum\limits_{k=0}^n\begin{bmatrix}k\\m\end{bmatrix}A_n^{n-k}\),选出 \(n-k\) 个元素和元素 \(n+1\) 分成一个圆,剩下的 \(k\) 个元素分成 \(m\) 个圆。
恒等式 4.8:\(\begin{Bmatrix}m+n+1\\m\end{Bmatrix}=\sum\limits_{k=0}^mk\begin{Bmatrix}n+k\\k\end{Bmatrix}\)。
这个式子从递推式入手(此处假设 \(n>k\)):
然后后面的也带进去:
继续带进去,直到 \(k=0\),整理一下就是恒等式 4.8 了。
恒等式 4.9:\(\begin{bmatrix}m+n+1\\m\end{bmatrix}=\sum\limits_{k=0}^m(n+k)\begin{bmatrix}n+k\\k\end{bmatrix}\)。
和 4.8 一个道理,只不过带进去的时候系数从 \(k\) 变成了 \(n+k\)。
恒等式 4.10:\(\dbinom nm=\sum\limits_k \begin{Bmatrix}n+1\\k+1\end{Bmatrix}\begin{bmatrix}k\\m\end{bmatrix}(-1)^{m-k}\)。
从 \(n\) 个元素里选择 \(m\) 个,同样先构建一个新的虚拟元素 \(n+1\),然后把这 \(n+1\) 个元素分成 \(k+1\) 个集合,然后把虚拟元素所在的集合删掉。
然后这 \(k\) 个集合全部选择集合中最小的元素,再组成 \(m\) 个环,然后每个环里面最大的元素选出来。
恒等式 4.11:\(n^{\underline{n-m}}[n\ge m]=\sum\limits_k\begin{bmatrix}n+1\\k+1\end{bmatrix}\begin{Bmatrix}k\\m\end{Bmatrix}(-1)^{m-k}\)
和 4.10 同个道理,从 \(n\) 个元素有序地选出 \(m\) 个,相当于构建一个虚拟元素之后,分成 \(k+1\) 个环,淘汰掉虚拟元素所在集合,再把每个集合用集合中最小元素代替,再分成 \(m\) 个集合,每个集合选最大元素。
恒等式 4.12:\(\begin{Bmatrix}n\\n-m\end{Bmatrix}=\sum\limits_k\dbinom {m-n}{m+k}\dbinom{m+n}{n+k}\begin{bmatrix}m+k\\k\end{bmatrix}\)。
恒等式 4.13:\(\begin{bmatrix}n\\n-m\end{bmatrix}=\sum\limits_k\dbinom {m-n}{m+k}\dbinom{m+n}{n+k}\begin{Bmatrix}m+k\\k\end{Bmatrix}\)。
因为我太蒻了,所以这两个恒等式我暂时不会证,如有会证明的欢迎分享!
upt:有一种比较高级的代数方法,感谢神仙 \(\text{\color{Red}{Alpha1022}}\) 提供的链接。
恒等式 4.14:\(\begin{Bmatrix}n\\l+m\end{Bmatrix}\dbinom{l+m}l=\sum\limits_k\begin{Bmatrix}k\\l\end{Bmatrix}\begin{Bmatrix}n-k\\m\end{Bmatrix}\dbinom nk\)。
这个比较好理解,相当于把 \(n\) 个元素分成两拨,一拨分成 \(l\) 组,另一拨分成 \(m\) 组,左边是先分组,再分拨,右边是先分拨,再分组。
恒等式 4.15:\(\begin{bmatrix}n\\l+m\end{bmatrix}\dbinom{l+m}l=\sum\limits_k\begin{bmatrix}k\\l\end{bmatrix}\begin{bmatrix}n-k\\m\end{bmatrix}\dbinom nk\)。
和 4.14 一样,就是把分组改成分环。