第一类斯特林数
定义
[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]=[n−1m−1]+(n−1)[n−1m]
因此可以 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=1∑n[ni]
一个排列本质上就是由若干个圆排列组成,于是我们可以枚举圆排列的数量。 - xn‾=∑i=0n[ni]xix^{\overline{n}}=\sum\limits_{i=0}^{n}\begin{bmatrix}n\\i\end{bmatrix}x^ixn=i=0∑n[ni]xi
其中,xn‾=x(x+1)...(x+n−1)x^{\overline{n}}=x(x+1)...(x+n-1)xn=x(x+1)...(x+n−1),称之为 xxx 的 nnn 次上升幂。对于这个式子,我们可以考虑归纳证明。考虑 k=n−1k=n-1k=n−1 时,xix^ixi 的系数,记作 f(n−1,i)f(n-1,i)f(n−1,i);k=nk=nk=n 时,乘多了个 x+n−1x+n-1x+n−1,那么 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(n−1,i−1)+(n−1)f(n−1,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=0∑n(−1)n−i[ni]xi
其中,xn‾=x(x−1)...(x−n+1)x^{\underline{n}}=x(x-1)...(x-n+1)xn=x(x−1)...(x−n+1),称之为 xxx 的 nnn 次下降幂。该式的证明,我就不证了。
第二类斯特林数
定义
{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}={n−1m−1}+m{n−1m}
因此可以 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=0∑nCmi{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=0∑n{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^5n≤105。
考虑生成函数 xn‾=∑i=0n[ni]xix^{\overline{n}}=\sum\limits_{i=0}^{n}\begin{bmatrix}n\\i\end{bmatrix}x^ixn=i=0∑n[ni]xi。
我们只需要快速求出 xn‾x^{\overline{n}}xn 的各项系数即可。可以用分治 FFTFFTFFT 在 O(nlog2n)O(nlog^2n)O(nlog2n) 求出。但是分治 NTTNTTNTT 太 naivenaivenaive 了,这个是可以倍增 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=0∑nfi(x+n)i=i=0∑nfij=0∑iCijxjni−j=j=0∑nj!xji=j∑n(i−j)!ni−ji!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=j∑n(i−j)!ni−ji!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^5n≤105。
设 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=0∑ni!(i−1)!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^5n≤105。
其实,第二类斯特林数还可以这样求。我们可以先给集合标序,然后再把方案数除掉 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=0∑m(−1)iCni(m−i)n。发现这是个卷积的形式,于是可以用 NTTNTTNTT 在 O(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^5n≤105。
和求第一类斯特林数的方法是一样的。
设 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=0∑ni!{i1}xi=i=0∑ni!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;