斯特林数
上下划线比较频繁,但是cnblogs显示不了。请主观猜测或者右键公式开SVG或者看LaTeX源码。
第二类斯特林数
第二类斯特林数 \(\begin{Bmatrix}n\\m\end{Bmatrix}\) 定义为将 \(1-n\) 分为 \(m\) 个集合的方案数。
有递推式:\(\begin{Bmatrix}n\\m\end{Bmatrix}=\begin{Bmatrix}n-1\\m-1\end{Bmatrix}+m\begin{Bmatrix}n-1\\m\end{Bmatrix}\) 。组合意义显然。
一些斯特林恒等式:
考虑组合意义。\(m^n\) 是把 \(n\) 个球放进 \(m\) 个盒子里面的方案数。枚举 \(i\) 个盒子有球,选出 \(i\) 个盒子的方案数是 \(\binom mi\),把 \(n\) 个球放进去的方案数是 \(\begin{Bmatrix}m\\i\end{Bmatrix}\),盒子不同所以乘 \(i!\)。
对上式二项式反演即可得到斯特林数的通项公式。
这是一斯特林数的 EGF。证明仍然是组合意义,右边是 \(k\) 个盒子的无序组合。
第一类斯特林数
第一类斯特林数表示 \(1-n\) 分成 \(m\) 个轮换的方案数(旋转后相同的为相同轮换)。
有递推式:\(\begin{bmatrix}n\\m\end{bmatrix}=\begin{bmatrix}n-1\\m-1\end{bmatrix}+(n-1)\begin{bmatrix}n-1\\m\end{bmatrix}\)。组合意义也挺显然的。
一列第一类斯特林数的 EGF:
斯特林反演
利用这个可以在普通幂和上升/下降幂之间转换。
有规律:普通转别的第一类,别的转普通第二类,大到小乘 \((-1)^{n-k}\)。
然后是怎么快速求一行或一列。
第二类斯特林数·行
我们有
二项式反演一下
嗯卷。
void getstiring(int n,int a[]){
for(int i=0;i<=n;i++){
a[i]=(i&1)?mod-inv[i]:inv[i];
b[i]=1ll*qpow(i,n)*inv[i]%mod;
}
n++;get(n<<1);
ntt(a,wl,1);ntt(b,wl,1);
for(int i=0;i<wl;i++)a[i]=1ll*a[i]*b[i]%mod;
ntt(a,wl,-1);
}
第一类斯特林数·行
显然
考虑怎么求这个东西。同样显然
直接套多项式平移即可。注意倍增的时候要分奇偶讨论一下,有时候要算 \(x^{\overline n}(x+n)\) ,暴力扫两遍乘就行了。
void getstiring(int n,int b[]){
if(n==1){
b[1]=1;return;
}
getstiring(n>>1,b);
move(b,(n>>1)+1,n>>1,a);
ntt(b,wl,1);ntt(a,wl,1);
for(int i=0;i<wl;i++)b[i]=1ll*b[i]*a[i]%mod;
ntt(b,wl,-1);
for(int i=n+1;i<wl;i++)b[i]=0;
if(n&1){
for(int i=n;i>=1;i--)b[i]=(b[i-1]+1ll*(n-1)*b[i]%mod)%mod;
b[0]=1ll*b[0]*(n-1)%mod;
}
}
第二类斯特林数·列
command_blocks 给出了一个优秀的 \(O(n\log n)\) 小常数解法。
当然你可以写个多项式快速幂直接套一列第二类斯特林数的生成函数
但是我们有不用 \(\exp\) 的方法。
套路设 \(F_l(x)=\sum_{i=0}\begin{Bmatrix}i\\k\end{Bmatrix}x^i\) ,然后开始化:
解方程得到
边界 \(F_0(x)=1\) ,所以
现在我们求 \(\prod_{i=1}^k1-ix\) 。提取公因式 \(x^k\) ,有
发现 \(\prod_{i=1}^k(x-i)\) 翻转之后就是 \(\prod_{i=1}^k(x^{-1}-i)\) 。然后 \(\prod_{i=1}^k(x-i)\) 显然是 \(\frac{x^{\underline {k+1}}}{x}\) 。这个也可以直接倍增多项式平移搞定。
void solve(int n,int b[]){
if(n==1){
b[1]=1;return;
}
solve(n>>1,b);
move(b,(n>>1)+1,mod-(n>>1),a);
ntt(b,wl,1);ntt(a,wl,1);
for(int i=0;i<wl;i++)b[i]=1ll*b[i]*a[i]%mod;
ntt(b,wl,-1);
for(int i=n+1;i<wl;i++)b[i]=a[i]=0;
if(n&1){
for(int i=n;i>=1;i--)b[i]=(b[i-1]+1ll*(mod-n+1)*b[i]%mod)%mod;
b[0]=1ll*b[0]*(mod-n+1)%mod;
}
}
void getstiring(int n,int k,int a[]){
if(n<k)return;
solve(k+1,b);
reverse(b,b+k+2);
for(int i=0;i<=n;i++)a[i]=0;
getinv(n-k+1,b,a);
for(int i=n;i>=k;i--)a[i]=a[i-k];
for(int i=0;i<k;i++)a[i]=0;
}
第一类斯特林数·列
第一类斯特林数的 \(\rm EGF\) :
其实里面那个 \(\ln\) 不用多项式 \(\ln\) ,直接按定义把每个系数放上去就行了。
void getstiring(int n,int a[],int k){
if(n<=k)return;
for(int i=0;i<n-k;i++)a[i]=qpow(i+1,mod-2);
getpow(n-k,a,b,k);
for(int i=n-k-1;i>=0;i--)a[i+k]=b[i];
for(int i=0;i<k;i++)a[i]=0;
int inv=qpow(jc[k],mod-2);
for(int i=0;i<n;i++)a[i]=1ll*a[i]*jc[i]%mod*inv%mod;
}
贝尔数
一行斯特林数的和。组合意义推 \(\rm EGF\) 可以得到生成函数
套。
void getbell(int n,int a[]){
for(int i=n-1;i>=1;i--)a[i]=inv[i]=1ll*inv[i+1]*(i+1)%mod;
getexp(n,a,b);
for(int i=0;i<n;i++)a[i]=1ll*b[i]*jc[i]%mod;
}