斯特林数及其性质

本文详细介绍了斯特林数的定义、性质以及如何高效计算第一类和第二类斯特林数,包括行与列的求解方法,并给出了利用生成函数和快速幂技巧的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一类斯特林数

定义

[nm]\begin{bmatrix}n\\m\end{bmatrix}[nm]s(n,m)s(n,m)s(n,m) ,表示 nnn 个不同的数分成 mmm 个无序圆排列的方案数。

这个是可以递推的,考虑最后一个数单独成环还是加入到前面的环中,有:
[nm]=[n−1m−1]+(n−1)[n−1m]\begin{bmatrix}n\\m\end{bmatrix}=\begin{bmatrix}n-1\\m-1\end{bmatrix}+(n-1)\begin{bmatrix}n-1\\m\end{bmatrix}[nm]=[n1m1]+(n1)[n1m]
因此可以 O(nm)O(nm)O(nm) 求出 {[ij]∣i∈[0,n],j∈[0,m]}\{\begin{bmatrix}i\\j\end{bmatrix}|i\in[0,n],j\in[0,m]\}{[ij]i[0,n],j[0,m]}

性质

  • n!=∑i=1n[ni]n!=\sum\limits_{i=1}^{n}\begin{bmatrix}n\\i\end{bmatrix}n!=i=1n[ni]
    一个排列本质上就是由若干个圆排列组成,于是我们可以枚举圆排列的数量。
  • xn‾=∑i=0n[ni]xix^{\overline{n}}=\sum\limits_{i=0}^{n}\begin{bmatrix}n\\i\end{bmatrix}x^ixn=i=0n[ni]xi
    其中,xn‾=x(x+1)...(x+n−1)x^{\overline{n}}=x(x+1)...(x+n-1)xn=x(x+1)...(x+n1),称之为 xxxnnn 次上升幂。对于这个式子,我们可以考虑归纳证明。考虑 k=n−1k=n-1k=n1 时,xix^ixi 的系数,记作 f(n−1,i)f(n-1,i)f(n1,i)k=nk=nk=n 时,乘多了个 x+n−1x+n-1x+n1,那么 f(n,i)=f(n−1,i−1)+(n−1)f(n−1,i)f(n,i)=f(n-1,i-1)+(n-1)f(n-1,i)f(n,i)=f(n1,i1)+(n1)f(n1,i),发现这就是第一类斯特林数的递推式呀。
  • xn‾=∑i=0n(−1)n−i[ni]xix^{\underline{n}}=\sum\limits_{i=0}^{n}(-1)^{n-i}\begin{bmatrix}n\\i\end{bmatrix}x^ixn=i=0n(1)ni[ni]xi
    其中,xn‾=x(x−1)...(x−n+1)x^{\underline{n}}=x(x-1)...(x-n+1)xn=x(x1)...(xn+1),称之为 xxxnnn 次下降幂。该式的证明,我就不证了。

第二类斯特林数

定义

{nm}\begin{Bmatrix}n\\m\end{Bmatrix}{nm}S(n,m)S(n,m)S(n,m),表示 nnn 个不同的数分成 mmm 个无序集合的方案数。
这个也是可以递推的,考虑最后一个数单独作为一个集合或者是放入之前的集合中,有:
{nm}={n−1m−1}+m{n−1m}\begin{Bmatrix}n\\m\end{Bmatrix}=\begin{Bmatrix}n-1\\m-1\end{Bmatrix}+m\begin{Bmatrix}n-1\\m\end{Bmatrix}{nm}={n1m1}+m{n1m}
因此可以 O(nm)O(nm)O(nm) 求出 {{ij}∣i∈[0,n],j∈[0,m]}\{\begin{Bmatrix}i\\j\end{Bmatrix}|i\in[0,n],j\in[0,m]\}{{ij}i[0,n],j[0,m]}

性质

  • mn=∑i=0nCmi{ni}i!m^n=\sum\limits_{i=0}^{n}C_m^i\begin{Bmatrix}n\\i\end{Bmatrix}i!mn=i=0nCmi{ni}i!
    考虑该式的组合意义:nnn 个不同的球,放入 mmm 个不同的盒子中,每个盒子可以空的方案数。我们可以放了球的盒子个数 iii,那么有 CmiC_m^iCmi 种选法,然后把 nnn 个球放进 iii 个不同盒子中,方案数为 {nm}i!\begin{Bmatrix}n\\m\end{Bmatrix}i!{nm}i!
  • 如果将上式的组合数乘进式子里,我们可以得到:
    mn=∑i=0n{ni}mi‾m^n=\sum\limits_{i=0}^{n}\begin{Bmatrix}n\\i\end{Bmatrix}m^{\underline{i}}mn=i=0n{ni}mi

求斯特林数

第一类斯特林数 · 行

[n0],[n1]...,[nn]\begin{bmatrix}n\\0\end{bmatrix},\begin{bmatrix}n\\1\end{bmatrix}...,\begin{bmatrix}n\\n\end{bmatrix}[n0],[n1]...,[nn],其中 n≤105n\leq 10^5n105
考虑生成函数 xn‾=∑i=0n[ni]xix^{\overline{n}}=\sum\limits_{i=0}^{n}\begin{bmatrix}n\\i\end{bmatrix}x^ixn=i=0n[ni]xi
我们只需要快速求出 xn‾x^{\overline{n}}xn 的各项系数即可。可以用分治 FFTFFTFFTO(nlog2n)O(nlog^2n)O(nlog2n) 求出。但是分治 NTTNTTNTTnaivenaivenaive 了,这个是可以倍增 NTTNTTNTT 来求的。
考虑从 xn‾x^{\overline{n}}xn 推到 x2n‾x^{\overline{2n}}x2n。首先显然有 x2n‾=xn‾(x+n)n‾x^{\overline{2n}}=x^{\overline{n}}(x+n)^{\overline{n}}x2n=xn(x+n)n
假设我们已经求出了 xn‾x^{\overline{n}}xn,记作 f(x)f(x)f(x),现在要求 f(x+n)f(x+n)f(x+n)
f(x+n)=∑i=0nfi(x+n)i=∑i=0nfi∑j=0iCijxjni−j=∑j=0nxjj!∑i=jnni−j(i−j)!i!fif(x+n)=\sum\limits_{i=0}^{n}f_i(x+n)^i=\sum\limits_{i=0}^{n}f_i\sum\limits_{j=0}^{i}C_i^jx^jn^{i-j}=\sum\limits_{j=0}^{n}\frac{x^j}{j!}\sum\limits_{i=j}^{n}\frac{n^{i-j}}{(i-j)!}i!f_if(x+n)=i=0nfi(x+n)i=i=0nfij=0iCijxjnij=j=0nj!xji=jn(ij)!niji!fi
后面那一部分是个卷积形式,记 gj=∑i=jnni−j(i−j)!i!fig_j=\sum\limits_{i=j}^{n}\frac{n^{i-j}}{(i-j)!}i!f_igj=i=jn(ij)!niji!fi,翻转一下 i!fii!f_ii!fi 后,我们可以用 NTTNTTNTT 快速求出 gjg_jgj
时间复杂度是 T(n)=T(n/2)+O(nlogn)=O(nlogn)T(n)=T(n/2)+O(nlogn)=O(nlogn)T(n)=T(n/2)+O(nlogn)=O(nlogn)

第一类斯特林数 · 列

[0k],[1k]...,[nk]\begin{bmatrix}0\\k\end{bmatrix},\begin{bmatrix}1\\k\end{bmatrix}...,\begin{bmatrix}n\\k\end{bmatrix}[0k],[1k]...,[nk],其中 n≤105n\leq 10^5n105
f(1,x)f(1,x)f(1,x)k=1k=1k=1[01],[11]...,[n1]\begin{bmatrix}0\\1\end{bmatrix},\begin{bmatrix}1\\1\end{bmatrix}...,\begin{bmatrix}n\\1\end{bmatrix}[01],[11]...,[n1]EGFEGFEGF,那么 f(1,x)=∑i=0n(i−1)!i!xif(1,x)=\sum\limits_{i=0}^{n}\frac{(i-1)!}{i!}x^if(1,x)=i=0ni!(i1)!xi。那么 f(k,n)=f(1,n)kk!f(k,n)=\frac{f(1,n)^k}{k!}f(k,n)=k!f(1,n)k,除掉 k!k!k! 是因为环之间是没有顺序的。
然后多项式快速幂即可,复杂度 O(nlogn)O(nlogn)O(nlogn)

第二类斯特林数 · 行

{n0},{n1}...,{nn}\begin{Bmatrix}n\\0\end{Bmatrix},\begin{Bmatrix}n\\1\end{Bmatrix}...,\begin{Bmatrix}n\\n\end{Bmatrix}{n0},{n1}...,{nn},其中 n≤105n\leq 10^5n105
其实,第二类斯特林数还可以这样求。我们可以先给集合标序,然后再把方案数除掉 m!m!m!,这样做可行的原因是没有非空集合,每一种填数方案都会重复 m!m!m! 次。所以根据容斥原理,我们可以枚举空的集合的个数 iii,然后有 {nm}=1m!∑i=0m(−1)iCni(m−i)n\begin{Bmatrix}n\\m\end{Bmatrix}={1 \over m!} \sum\limits_{i=0}^{m}(-1)^iC_n^i(m-i)^n{nm}=m!1i=0m(1)iCni(mi)n。发现这是个卷积的形式,于是可以用 NTTNTTNTTO(nlogn)O(nlogn)O(nlogn) 求出。

第二类斯特林数 · 列

{0k},{1k}...,{nk}\begin{Bmatrix}0\\k\end{Bmatrix},\begin{Bmatrix}1\\k\end{Bmatrix}...,\begin{Bmatrix}n\\k\end{Bmatrix}{0k},{1k}...,{nk},其中 n≤105n\leq 10^5n105
和求第一类斯特林数的方法是一样的。
f(1,x)f(1,x)f(1,x)k=1k=1k=1{01},{11}...,{n1}\begin{Bmatrix}0\\1\end{Bmatrix},\begin{Bmatrix}1\\1\end{Bmatrix}...,\begin{Bmatrix}n\\1\end{Bmatrix}{01},{11}...,{n1}EGFEGFEGF,那么 f(1,x)=∑i=0n{i1}i!xi=∑i=0n1i!xif(1,x)=\sum\limits_{i=0}^{n}\frac{\tiny{\begin{Bmatrix}i\\1\end{Bmatrix}}}{i!}x^i=\sum\limits_{i=0}^{n}\frac{1}{i!}x^if(1,x)=i=0ni!{i1}xi=i=0ni!1xi。那么 f(k,n)=f(1,n)kk!f(k,n)=\frac{f(1,n)^k}{k!}f(k,n)=k!f(1,n)k,除掉 k!k!k! 是因为集合之间是没有顺序的。
然后多项式快速幂即可,复杂度 O(nlogn)O(nlogn)O(nlogn)

代码

namespace Stiring{
	const int M = 3e5 + 5;
	int fac[M], invf[M];
	void init(int n){
		fac[0] = 1;
		for(int i = 1; i <= n; i++) fac[i] = (LL)fac[i - 1] * i % mod;
		invf[n] = power(fac[n], mod - 2);
		for(int i = n - 1; i >= 0; i--) invf[i] = (LL)invf[i + 1] * (i + 1) % mod;
	}
	namespace Stiring1{
		poly Get(poly f, int c){//f(x) -> f(x + c)
			poly g(c + 1), h(c + 1);
			for(int i = 0, ck = 1; i <= c; i++, ck = (LL)ck * c % mod) g[i] = (LL)fac[c - i] * f[c - i] % mod, h[i] = (LL)ck * invf[i] % mod;
			g = g * h;
			for(int i = 0; i <= c; i++) h[i] = (LL)invf[i] * g[c - i] % mod;
			return h;
		}
		poly Solve(int n){//x^(up n)
			if(!n) return {1};
			int m = n / 2;
			poly f = Solve(m), g = Get(f, m);
			f = f * g;
			f.resize(n + 1);
			if(n & 1){
				for(int i = n; i >= 0; i--){
					f[i] = (LL)f[i] * (n - 1) % mod;
					if(i > 0) f[i] = (f[i] + f[i - 1]) % mod; 
				}
			}
			return f;
		}
		poly Stiring1_row(int n){
			init(n);
			return Solve(n);
		}
		poly Stiring1_line(int n, int k){
			poly f(n + 1);
			int fack = 1;
			for(int i = 1; i <= k; i++) fack = (LL)fack * i % mod;
			for(int i = 0; i <= n; i++) f[i] = power(i, mod - 2);
			poly_pow2(f, k, 0, k);
			for(int i = 0, fac = 1; i <= n; i++, fac = (LL)fac * i % mod) f[i] = (LL)f[i] * power(fack, mod - 2) % mod * fac % mod;
			f.resize(n + 1);
			return f;
		}
	}using namespace Stiring1;
	namespace Stiring2{
		poly Stiring2_row(int n){
			poly f(n + 1), g(n + 1);
			init(n);
			for(int i = 0; i <= n; i++) f[i] = (LL)power(mod - 1, i) * power(fac[i], mod - 2) % mod, g[i] = (LL)power(i, n) * power(fac[i], mod - 2) % mod;
			f = f * g;
			f.resize(n + 1);
			return f;
		}
		poly Stiring2_line(int n, int k){
			poly f(n + 1);
			int fack = 1;
			for(int i = 1; i <= k; i++) fack = (LL)fack * i % mod;
			for(int i = 1, fac = 1; i <= n; i++, fac = (LL)fac * i % mod) f[i] = power(fac, mod - 2);
			poly_pow2(f, k, 0, k);
			for(int i = 0, fac = 1; i <= n; i++, fac = (LL)fac * i % mod) f[i] = (LL)f[i] * power(fack, mod - 2) % mod * fac % mod;
			f.resize(n + 1);
			return f;
		}
	}using namespace Stiring2;
}
using namespace Stiring;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值