第一类斯特林数
定义
s(i,j)表示把i个物品分成j个环的方案,或者记做[ij]
递推式
考虑第i个物品的划分
s(i,j)=s(i−1,j−1)+s(i−1,j)∗(i−1)
枚举第1个物品所在环的大小
s(i,j)=∑k=1n(k−1n−1)(k−1)!s(i−k,j−1)
快速求s(n,i)
考虑构造生成函数Fn=∑i=0∞s(n,i)xi
由递推式可得
Fn=xFn−1+(n−1)F(n−1)=(x+n−1)F(n−1)
可得Fn=xn
即∑i=0ns(n,i)xi=xn
这是就是第一类斯特林数和上升幂的关系
而且也有关于下降幂,由于xn=(x+n)n
所以xn=(x−n)n
所以∑i=0n(−1)n−is(n,i)xi=xn=(nx)n!
对和上升幂的式子
分治NTT可以做到O(nlog2n)
考虑倍增
如果n为奇数,递归求解n−1,乘一个x+n−1
如果n为偶数
有x2n=xn(x+n)n
假设已经求出了xn=f1(x)=∑i=0naixi
考虑如何求出
f2(x)=∑i=0nai(x+n)i
f2(x)=∑i=0nai∑j=0ixjni−j(ji)
=∑j=0nj!xj∑i=jnaii!(i−j)!ni−j
=∑j=0nj!xj∑i=0n−jai+j(i+j)!i!ni
令A(x)=∑i=0naii!xi,B(x)=∑i=0n(n−i)!nn−ixi
把(A∗B)(x)乘出来左移n位即可
复杂度T(n)=T(2n)+O(nlogn)=O(nlogn)
代码:
inline poly calc_s(int n){
poly res;
if(n==1){res.pb(0),res.pb(1);return res;}
if(n&1){
res=calc_s(n-1);
res.resize(n+1);
for(int i=n;i;i--)res[i]=add(res[i-1],mul(res[i],n-1));
return res;
}
int mid=n>>1;
poly a=calc_s(mid),b(mid+1),c(mid+1);
for(int i=0;i<=mid;i++)c[i]=mul(a[i],fac[i]);
for(int i=0,p=1;i<=mid;i++,Mul(p,mid))b[i]=mul(p,ifac[i]);
reverse(b.bg(),b.bg()+mid+1);
c=c*b;for(int i=0;i<=mid;i++)c[i]=mul(c[i+mid],ifac[i]);
c.resize(mid+1),res=a*c;return res;
}
例题:传送门
第一类斯特林数快速求行
第一类斯特林数快速求列
第二类斯特林数
定义
S(i,j)表示把i个物品分成j个集合的方案数,也记作{nm}
递推式
考虑第i个数的划分
S(i,j)=S(i−1,j−1)+j∗S(i−1,j)
考虑第一个数所在的集合大小
S(i,j)=∑k=1i(k−1i−1)S(i−k,j−1)
快速求解S(n,x)
考虑xn是用x种颜色去染n个格子,枚举最后用了几种颜色,可得
xn=∑i=1min(x,n)(ix)i!S(n,i)
实际上由于(ji)和S(i,j)在i<j的时候都为0
所以写作
xn=∑i=1n(ix)i!S(n,i)
和
xn=∑i=1x(ix)i!S(n,i)
都可以
在实际题目中可能会根据不同情况选择变量
对第二个式子二项式反演
得
x!S(n,x)=∑i=1x(−1)x−i(ix)in
得
S(n,x)=∑i=1x(x−i)!(−1)x−ii!in
这是一个卷积的形式,NTT可以O(nlogn)求出
poly f,g;
for(int i=0;i<=n;i++)f.pb((i&1)?mod-ifac[i]:ifac[i]);
for(int i=0;i<=n;i++)g.pb(mul(ksm(i,n),ifac[i]));
poly S=f*g;
第二类斯特林数快速求行
第二类斯特林数快速求列