第一类斯特林数
(egin{bmatrix}n\ k end{bmatrix}) 表示(n) 个有区别的元素划分成(k) 个环的方案数。
(egin{bmatrix}n\ 1 end{bmatrix}=(n-1)!) 此时的每种轮换对应(n) 个循环同构的排列。
(egin{bmatrix}n\ n end{bmatrix} = 1 ,egin{bmatrix}n\ n-1 end{bmatrix} = {n choose 2}) 因为当每个轮换都是单元素或双元素时,轮换与子集是等价的。
(egin{bmatrix}n\ k end{bmatrix}=(n-1)egin{bmatrix}n-1\ k end{bmatrix}+egin{bmatrix}n-1\ k-1 end{bmatrix},ngeq1)
(n! = sumlimits_{k=0}^n egin{bmatrix}n\ k end{bmatrix}) 轮换与排列是一一对应的(其实一个排列类似一个置换)。
(x^{overline n}=sumlimits_{k} egin{bmatrix}n\ k end{bmatrix} x^k ,ngeq 0)
(x^{underline n}= sumlimits_{k} egin{bmatrix}n\ k end{bmatrix} (-1)^{n-k} x^k ,ngeq 0)
求解
求解(egin{bmatrix}n\ k end{bmatrix})
由于(x^{overline n}=sumlimits_{k=0}^negin{bmatrix}n\ kend{bmatrix} x^k) ,所以我们可以直接通过分治FFT求(x^{overline n}) 在(mathcal{O}(nlog^2n)) 求出。
优化:
当我们求完(x^{overline n}) 后,如果可以直接求出((x+n)^{overline n}) ,那么可以直接得出(x^{overline{2n}}) ,时间复杂度则降为(T(n)=T(frac{n}{2})+mathcal{O}(nlog n)=mathcal{O}(nlog n)) 。
设(x^{overline n}=sumlimits_{k=0}^n a_ix^i) ,
则
(egin{aligned}(x+n)^{overline n}&=sum_{i=0}^n a_i(x+n)^i\&=sum_{i=0}^n a^i sum_{j=0}^i {i choose j}x^j n^{i-j} \&=sum_{i=0}^n x^i sum_{j=i}^n {j choose i} a^j n^{j-i}\&=sum_{i=0}^nfrac{x^i}{i!}sum_{j=i}^nfrac{n^{j-i}}{(j-i)!} j!a_j end{aligned})
后面的部分是一个减法卷积,我们可以反转其中一个变成加法卷积即可。
#include<iostream>
#include<cstdio>
#define R register int
using namespace std;
namespace Luitaryi {
const int N=530000,G=3,Gi=55924054,M=167772161;
inline int qpow(int a,int b) { R ret=1;
for(;b;b>>=1,a=1ll*a*a%M) if(b&1) ret=1ll*ret*a%M; return ret;
}
int n,K,len,p[N],fac[N],ifac[N];
inline void init(int n) {
K=1,len=0; while(K<=n) K<<=1,++len;
for(R i=1;i<K;++i) p[i]=(p[i>>1]>>1)|((i&1)<<(len-1));
}
inline void ntt(int* a,int op) {
for(R i=1;i<K;++i) if(i<p[i]) swap(a[i],a[p[i]]);
for(R l=1;l<K;l<<=1) {
R w1=qpow(~op?G:Gi,(M-1)/(l<<1)),wn,x,y;
for(R len=l<<1,i=0;i<K;i+=len) { wn=1;
for(R j=0;j<l;++j,wn=1ll*w1*wn%M)
x=a[i+j],y=1ll*a[i+j+l]*wn%M,a[i+j]=(x+y)%M,a[i+j+l]=(x-y+M)%M;
}
} if(~op) return ; R Inv=qpow(K,M-2);
for(R i=0;i<K;++i) a[i]=1ll*a[i]*Inv%M;
}
int f[N],a[N],b[N],ta[N],tb[N];
inline void calc(const int* f,int n,int* h) {
init(n<<1);
for(R i=0;i<=n;++i) ta[n-i]=1ll*f[i]*fac[i]%M;
for(R t=1,i=0;i<=n;++i,t=1ll*t*n%M)
tb[i]=1ll*ifac[i]*t%M;
for(R i=n+1;i<K;++i) ta[i]=tb[i]=0;
ntt(ta,1),ntt(tb,1);
for(R i=0;i<K;++i) ta[i]=1ll*ta[i]*tb[i]%M;
ntt(ta,-1);
for(R i=0;i<=n;++i) h[i]=1ll*ta[n-i]*ifac[i]%M;
}
inline void solve(int n,int* f) {
if(n==0) return ;
R md=n>>1;
solve(md,f);
calc(f,md,a);
init(n);
for(R i=0;i<=md;++i) b[i]=f[i];
for(R i=md+1;i<K;++i) a[i]=b[i]=0;
ntt(a,1),ntt(b,1);
for(R i=0;i<K;++i) a[i]=1ll*a[i]*b[i]%M;
ntt(a,-1);
if(n&1) for(R i=0;i<=n;++i)
f[i]=((i?a[i-1]:0)+1ll*(n-1)*a[i])%M;
else for(R i=0;i<=n;++i) f[i]=a[i];
}
inline void main() {
scanf("%d",&n);
f[0]=fac[0]=fac[1]=ifac[0]=ifac[1]=1;
for(R i=2;i<=n;++i) fac[i]=1ll*fac[i-1]*i%M;
ifac[n]=qpow(fac[n],M-2);
for(R i=n-1;i>=2;--i) ifac[i]=1ll*ifac[i+1]*(i+1)%M;
solve(n,f);
for(R i=0;i<=n;++i)
printf("%d ",f[i]);
}
} signed main() {Luitaryi::main(); return 0;}
第二类斯特林数
(egin{Bmatrix}n\ k end{Bmatrix}) 表示(n) 个有区别的元素划分成(k) 个非空子集的方案数。
(egin{Bmatrix}0\ 0 end{Bmatrix}=1)
(egin{Bmatrix}n\ 0 end{Bmatrix}=0 ,ngeq 1)
(egin{Bmatrix}n\ 1 end{Bmatrix}=1 ,ngeq 1)
(egin{Bmatrix}n\ 2 end{Bmatrix}=2^{n-1}-1 ,ngeq 1) 强制至少一个元素在集合(A) ,剩下(2^{n-1}) 种方案,减去集合(B) 是空集的方案。
(egin{Bmatrix}n\ 0 end{Bmatrix}=0 ,ngeq 1)
(egin{Bmatrix}n\ k end{Bmatrix}=kegin{Bmatrix}n-1\ k end{Bmatrix}+egin{Bmatrix}n-1\ k-1 end{Bmatrix} ,ngeq 1)
(egin{Bmatrix}n\ n end{Bmatrix} = 1 ,egin{Bmatrix}n\ n-1 end{Bmatrix} = {n choose 2})
性质
(x^{n}=sumlimits_{k} egin{Bmatrix}n\ k end{Bmatrix} x^{underline k} ,ngeq 0)
(x^{n}= sumlimits_{k} egin{Bmatrix}n\ k end{Bmatrix} (-1)^{n-k} x^{overline k} ,ngeq 0)
顺带:(x^{overline n}=(-1)^n(-x)^{underline n},x^{underline n}=(-1)^n(-x)^{overline n})
(x^n=sumlimits_{k=0}^x egin{Bmatrix}n\ k end{Bmatrix} k!{x choose k}) ,即(x^n=sumlimits_{k=0}^x egin{Bmatrix}n\ k end{Bmatrix} imes x^{underline k})
(x^n) 为(n) 个有区别的小球丢进(x) 个有区别的盒子,允许空盒子;枚举有效盒子的个数,再从(x) 个盒子选(k) 个盒子,然后(n) 个小球丢进(k) 个盒子。
(m^n=sumlimits_{i=0}^{m}egin{Bmatrix}n\ iend{Bmatrix}i!{m choose i})
二项式反演一下:
(m!egin{Bmatrix}n\ mend{Bmatrix}=sumlimits_{k=0}^m(-1)^{k} {mchoose k}(m-k)^n)
理解:如果空箱子的情况我们也算进去,答案显然是(frac{m^n}{m!}) ;反过来求第二类斯特林数,又得减掉这种情况:
- 选(k) 个空盒子,然后小球放到其他的盒子里
- 但最后我们求出来的答案为有区别的盒子,转换过来要( imes frac{1}{m!})
求解
求解(egin{Bmatrix}n\ k end{Bmatrix})
(egin{aligned}egin{Bmatrix}n\ mend{Bmatrix}&=frac{1}{m!}sumlimits_{k=0}^m(-1)^kfrac{m!}{k!(m-k)!}(m-k)^n\ &=sumlimits_{k=0}^mfrac{(-1)^k(m-k)^n}{k!(m-k)!}end{aligned})
发现可以卷积求出(n) 一行的斯特林数。
#include<iostream>
#include<cstdio>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
register char s; while(!isdigit(s=getchar())) f=s=='-'?-1:f;
do x=x*10+(s^48); while(isdigit(s=getchar())); return x*f;
} const int N=800010,G=3,Gi=55924054,M=167772161;
int n,K,len,ans;
int fac[N],Inv[N],a[N],b[N],p[N];
inline int qpow(int a,int b) { R ret=1;
for(;b;b>>=1,a=1ll*a*a%M) if(b&1) ret=1ll*ret*a%M; return ret;
}
inline void ntt(int* a,int op) {
for(R i=0;i<K;++i) if(i<p[i]) swap(a[i],a[p[i]]);
for(R l=1;l<K;l<<=1) { R g1=qpow(~op?G:Gi,(M-1)/(l<<1)),gn,x,y;
for(R len=l<<1,i=0;i<K;i+=len) { gn=1;
for(R j=0;j<l;++j,gn=1ll*gn*g1%M)
x=a[i+j],y=1ll*a[i+j+l]*gn%M,a[i+j]=(x+y)%M,a[i+j+l]=(x-y+M)%M;
}
}
}
inline void main() {
n=g();
fac[0]=fac[1]=Inv[0]=Inv[1]=1;
for(R i=2;i<=n;++i) fac[i]=1ll*fac[i-1]*i%M;
Inv[n]=qpow(fac[n],M-2);
for(R i=n-1;i>=2;--i) Inv[i]=1ll*Inv[i+1]*(i+1)%M;
K=1; while(K<=n*2) K<<=1,++len;
for(R i=0;i<K;++i) p[i]=(p[i>>1]>>1)|((i&1)<<(len-1));
for(R i=0;i<=n;++i) a[i]=1ll*(i&1?M-1ll:1ll)*Inv[i]%M;
for(R i=0;i<=n;++i) b[i]=1ll*qpow(i,n)*Inv[i]%M;
ntt(a,1),ntt(b,1);
for(R i=0;i<K;++i) a[i]=1ll*a[i]*b[i]%M;
ntt(a,-1); R inv=qpow(K,M-2);
for(R i=0;i<=n;++i) a[i]=1ll*a[i]*inv%M;
for(R i=0;i<=n;++i) printf("%d ",a[i]);
}
} signed main() {Luitaryi::main(); return 0;}
与自然数幂的关系
(egin{aligned} Sum(n)&=sumlimits_{i=0}^n i^k\ &=sumlimits_{i=0}^nsumlimits_{j=0}^kegin{Bmatrix}k\ j end{Bmatrix}i^{underline j}\ &=sumlimits_{j=0}^kegin{Bmatrix}k\ j end{Bmatrix}sumlimits_{i=0}^n i^{underline j}\ &=sumlimits_{j=0}^k egin{Bmatrix}k\ j end{Bmatrix}j!sumlimits_{i=0}^n{ichoose j}\ &=sumlimits_{j=0}^k egin{Bmatrix}k\ j end{Bmatrix}j!{n+1 choose j+1}\ &=sumlimits_{j=0}^k egin{Bmatrix}k\ j end{Bmatrix} frac{(n+1)^{underline {j+1}}}{j+1}end{aligned})
斯特林反演
反转公式
(sumlimits_{k=m}^n (-1)^{n-k}egin{bmatrix}n\ kend{bmatrix} egin{Bmatrix}k\ mend{Bmatrix}=[m=n]\ sumlimits_{k=m}^n (-1)^{n-k}egin{Bmatrix}n\ kend{Bmatrix} egin{bmatrix}k\ mend{bmatrix}=[m=n])
证明:
(egin{aligned}m^{underline n}&=sumlimits_{i=0}^n egin{bmatrix}n\ iend{bmatrix}(-1)^{n-i}m^i\ &=sumlimits_{i=0}^n egin{bmatrix}n\ iend{bmatrix}(-1)^{n-i}sumlimits_{j=0}^i egin{Bmatrix}i\ jend{Bmatrix}m^{underline j}\ &=sumlimits_{i=0}^n m^{underline i}sumlimits_{j=i}^n (-1)^{n-j} egin{bmatrix}n\ jend{bmatrix} egin{Bmatrix}j\ iend{Bmatrix}end{aligned})
(egin{aligned}m^n&=sumlimits_{i=0}^negin{Bmatrix}n\ iend{Bmatrix}m^{underline i}\ &=sumlimits_{i=0}^negin{Bmatrix}n\ iend{Bmatrix}(-1)^i(-m)^{overline i}\ &=sumlimits_{i=0}^negin{Bmatrix}n\ iend{Bmatrix}(-1)^isumlimits_{j=0}^i egin{bmatrix}i\ jend{bmatrix}(-m)^j\ &=sumlimits_{i=0}^n m^isumlimits_{j=i}^n(-1)^{i-j} egin{Bmatrix}n\ jend{Bmatrix}egin{bmatrix}j\ iend{bmatrix}end{aligned})
斯特林反演
设(g_n=sumlimits_{k=0}^n(-1)^{n-k}egin {bmatrix} n\ k end{bmatrix}f_k) 。
(egin{aligned} f_n&=sumlimits_{k=0}^n [k=n]f_k\ &=sumlimits_{k=0}^nsumlimits_{j=k}^n egin {Bmatrix} n\ j end{Bmatrix}egin {bmatrix} j\ k end{bmatrix}(-1)^{j-k}f_k\ &=sumlimits_{k=0}^n egin {Bmatrix} n\ k end{Bmatrix}sumlimits_{j=0}^k (-1)^{k-j}egin {bmatrix} k\ j end{bmatrix}f_j\ &=sumlimits_{k=0}^n egin {Bmatrix} n\ k end{Bmatrix}g_k end{aligned})