====== 斯特林数 ====== ===== 第一类斯特林数 ===== ==== 定义 ==== 第一类斯特林数 $\begin{bmatrix}n\\ k\end{bmatrix}$ 表示将 $n$ 个不同元素构成 $m$ 个圆排列的方案。 ==== 性质一 ==== $$\begin{bmatrix}n\\ k\end{bmatrix}=\begin{bmatrix}n-1\\ k-1\end{bmatrix}+(n-1)\begin{bmatrix}n-1\\ k\end{bmatrix}$$ 考虑新加入的数 $n$,要么单独成环,要么插入到其他环中,其中插入方式有 $n-1$ 种。 ==== 性质二 ==== $$x^{\overline n}=\sum_{i=0}^n \begin{bmatrix}n\\ i\end{bmatrix}x^i$$ 其中 $x^{\overline n}$ 表示上升幂。 考虑归纳证明,有 $$x^{\overline {n+1}}=x^{\overline n}(x+n)=(x+n)\sum_{i=0}^n \begin{bmatrix}n\\ i\end{bmatrix}x^i=\sum_{i=0}^{n+1} \left(\begin{bmatrix}n\\ i-1\end{bmatrix}+n\begin{bmatrix}n\\ i\end{bmatrix}\right)x^i=\sum_{i=0}^{n+1} \begin{bmatrix}n+1\\ i\end{bmatrix}x^i$$ ==== 性质三 ==== $$x^{\underline n}=\sum_{i=0}^n \begin{bmatrix}n\\ i\end{bmatrix}(-1)^{n-i}x^i$$ 其中 $x^{\underline n}$ 表示下降幂。 考虑归纳证明,有 $$x^{\underline {n+1}}=x^{\underline n}(x-n)=(x-n)\sum_{i=0}^n \begin{bmatrix}n\\ i\end{bmatrix}(-1)^{n-i}x^i=\sum_{i=0}^{n+1} \left(-\begin{bmatrix}n\\ i-1\end{bmatrix}-n\begin{bmatrix}n\\ i\end{bmatrix}\right)(-1)^{n-i}x^i=\sum_{i=0}^{n+1} \begin{bmatrix}n+1\\ i\end{bmatrix}(-1)^{n+1-i}x^i$$ ==== 运算 ==== === 第一类斯特林数$\cdot$行 === [[https://www.luogu.com.cn/problem/P5408|洛谷p5408]] $$\begin{bmatrix}n\\ i\end{bmatrix}(0\le i\le n)$$ 考虑倍增法,假设已知 $x^{\overline n}$,显然可以 $O(n)$ 计算出 $x^{\overline {n+1}}=(x+n)x^{\overline n}$。 接下来考虑求解 $x^{\overline {2n}}$,有 $x^{\overline {2n}}=x^{\overline n}(x+n)^{\overline n}$。设 $x^{\overline n}=\sum_{i=0}^n a_ix^i$,于是有 $$(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}n^{i-j}x^j=\sum_{j=0}^n x^j\sum_{i=j}^n a_i{i\choose j}n^{i-j}$$ 展开组合数,有 $$\sum_{j=0}^n x^j\sum_{i=j}^n a_i{i\choose j}n^{i-j}x^j=\sum_{j=0}^n \frac {x^j}{j!}\sum_{i=j}^n a_ii!\frac {n^{i-j}}{(i-j)!}=\sum_{j=0}^n \frac {x^j}{j!}\sum_{i=0}^{n-j} a_{i+j}(i+j)!\frac {n^i}{i!}$$ 设 $b_i=a_ii!,c_i=\cfrac {n^{n-i}}{(n-i)!}$,于是有 $$\sum_{j=0}^n \frac {x^j}{j!}\sum_{i=0}^{n-j} a_{i+j}(i+j)!\frac {n^i}{i!}=\sum_{j=0}^n \frac {x^j}{j!}\sum_{i=0}^{n-j} b_{i+j}c_{n-i}$$ 于是可以 $O(n\log n)$ 计算出 $(x+n)^{\overline n}$,最后与 $x^{\overline n}$ 卷积即可 $O(n\log n)$ 计算出 $x^{\overline {2n}}$。 总时间复杂度 $T(n)=T(\frac n2)+O(n\log n)$,于是 $T(n)=O(n\log n)$。 const int MAXN=1<<18,Mod=167772161,G=3; int quick_pow(int a,int b){ int ans=1; while(b){ if(b&1) ans=1LL*ans*a%Mod; a=1LL*a*a%Mod; b>>=1; } return ans; } namespace Poly{ const int G=3; int rev[MAXN<<2],Wn[30][2]; void init(){ int m=Mod-1,lg2=0; while(m%2==0)m>>=1,lg2++; Wn[lg2][1]=quick_pow(G,m); Wn[lg2][0]=quick_pow(Wn[lg2][1],Mod-2); while(lg2){ m<<=1,lg2--; Wn[lg2][0]=1LL*Wn[lg2+1][0]*Wn[lg2+1][0]%Mod; Wn[lg2][1]=1LL*Wn[lg2+1][1]*Wn[lg2+1][1]%Mod; } } int build(int k){ int n,pos=0; while((1<>1]>>1)|((i&1)<<(pos-1)); return n; } void NTT(int *f,int n,bool type){ _for(i,0,n)if(i=0;i--) f[i+1]=(f[i]+1LL*f[i+1]*n)%Mod; } void Mul(int n){ _rep(i,0,n)g[i]=1LL*f[i]*frac[i]%Mod; pown[0]=1; _rep(i,1,n)pown[i]=1LL*pown[i-1]*n%Mod; _rep(i,0,n)h[i]=1LL*pown[n-i]*invfrac[n-i]%Mod; Poly::Mul(g,n+1,h,n+1); _rep(i,0,n)g[i]=1LL*g[n+i]*invfrac[i]%Mod; Poly::Mul(f,n+1,g,n+1); } int main() { Poly::init(); int n=read_int(),pos=18,cur=1; while(n<(1<>pos)&1){ Add(cur); cur|=1; } } _rep(i,0,n) space(f[i]); return 0; } === 第一类斯特林数$\cdot$列 === [[https://www.luogu.com.cn/problem/P5409|洛谷p5409]] $$\begin{bmatrix}i\\ k\end{bmatrix}(0\le i\le n)$$ 考虑只有一个圆排列的方案的 $\text{EGF}$,有 $F(x)=\sum_{i=1}^{\infty} (i-1)!\cfrac {x^i}{i!}$。 于是 $k$ 个圆排列的 $\text{EGF}$ 为 $\cfrac {F^k(x)}{k!}$,利用 $\text{Exp}$ 和 $\text{Ln}$ 可以 $O(n\log n)$ 计算。 const int MAXN=1<<16,Mod=167772161,G=3; int quick_pow(int a,int b){ int ans=1; while(b){ if(b&1) ans=1LL*ans*a%Mod; a=1LL*a*a%Mod; b>>=1; } return ans; } namespace Poly{ const int G=3; int rev[MAXN<<2],Wn[30][2]; void init(){ int m=Mod-1,lg2=0; while(m%2==0)m>>=1,lg2++; Wn[lg2][1]=quick_pow(G,m); Wn[lg2][0]=quick_pow(Wn[lg2][1],Mod-2); while(lg2){ m<<=1,lg2--; Wn[lg2][0]=1LL*Wn[lg2+1][0]*Wn[lg2+1][0]%Mod; Wn[lg2][1]=1LL*Wn[lg2+1][1]*Wn[lg2+1][1]%Mod; } } int build(int k){ int n,pos=0; while((1<>1]>>1)|((i&1)<<(pos-1)); return n; } void NTT(int *f,int n,bool type){ _for(i,0,n)if(i>1); int n=build((_n-1)<<1); _for(i,(_n+1)>>1,n)g[i]=0; _for(i,0,_n)temp[i]=f[i];_for(i,_n,n)temp[i]=0; NTT(g,n,true);NTT(temp,n,true); _for(i,0,n)g[i]=(2-1LL*temp[i]*g[i]%Mod)*g[i]%Mod; NTT(g,n,false); _for(i,_n,n)g[i]=0; } void Ln(const int *f,int *g,int _n){ static int temp[MAXN<<2]; Inv(f,g,_n); _for(i,1,_n)temp[i-1]=1LL*f[i]*i%Mod; temp[_n-1]=0; Mul(g,_n,temp,_n-1,_n); for(int i=_n-1;i;i--)g[i]=1LL*g[i-1]*quick_pow(i,Mod-2)%Mod; g[0]=0; } void Exp(const int *f,int *g,int _n){ static int temp[MAXN<<2]; if(_n==1)return g[0]=1,void(); Exp(f,g,(_n+1)>>1); _for(i,(_n+1)>>1,_n)g[i]=0; Ln(g,temp,_n); temp[0]=(1+f[0]-temp[0])%Mod; _for(i,1,_n)temp[i]=(f[i]-temp[i])%Mod; Mul(g,(_n+1)>>1,temp,_n,_n); } void Pow(const int *f,int *g,int _n,int k1,int k2){ static int temp[MAXN<<2]; int pos=0,posv; while(!f[pos]&&pos<_n)pos++; if(1LL*pos*k2>=_n){ _for(i,0,_n)g[i]=0; return; } posv=quick_pow(f[pos],Mod-2); _for(i,pos,_n)g[i-pos]=1LL*f[i]*posv%Mod; _for(i,_n-pos,_n)g[i]=0; Ln(g,temp,_n); _for(i,0,_n)temp[i]=1LL*temp[i]*k1%Mod; Exp(temp,g,_n); pos=pos*k2,posv=quick_pow(posv,1LL*k2*(Mod-2)%(Mod-1)); for(int i=_n-1;i>=pos;i--)g[i]=1LL*g[i-pos]*posv%Mod; _for(i,0,pos)g[i]=0; } } int f[MAXN<<2],g[MAXN<<2],frac[MAXN<<1],invfrac[MAXN<<1]; int main() { Poly::init(); int n=read_int(),k=read_int(); frac[0]=1; _rep(i,1,n)frac[i]=1LL*frac[i-1]*i%Mod; invfrac[n]=quick_pow(frac[n],Mod-2); for(int i=n;i;i--)invfrac[i-1]=1LL*invfrac[i]*i%Mod; _rep(i,1,n)f[i]=1LL*frac[i-1]*invfrac[i]%Mod; Poly::Pow(f,g,n+1,k,k); _rep(i,0,n)g[i]=1LL*g[i]*frac[i]%Mod*invfrac[k]%Mod; _rep(i,0,n)space(g[i]); return 0; } ===== 第二类斯特林数 ===== ==== 定义 ==== 第二类斯特林数 $\begin{Bmatrix}n\\ k\end{Bmatrix}$ 表示将 $n$ 个不同元素划分到 $m$ 个非空集的方案数。 ==== 性质一 ==== $$\begin{Bmatrix}n\\ k\end{Bmatrix}=\begin{Bmatrix}n-1\\ k-1\end{Bmatrix}+k\begin{Bmatrix}n-1\\ k\end{Bmatrix}$$ 考虑新加入的数 $n$,要么单独划分成一个集合,要么加入到其他集合中,其中加入方式有 $k$ 种。 ==== 性质二 ==== $$x^n=\sum_{i=0}^n \begin{Bmatrix}n\\ i\end{Bmatrix}(-1)^{n-i}x^{\overline i}$$ 其中 $x^{\overline i}$ 表示上升幂。 考虑归纳证明,有 $$x^{n+1}=x\sum_{i=0}^n \begin{Bmatrix}n\\ i\end{Bmatrix}(-1)^{n-i}x^{\overline i}=\sum_{i=0}^n \begin{Bmatrix}n\\ i\end{Bmatrix}(-1)^{n-i}\left(x^{\overline {i+1}}-ix^{\overline i}\right)=\sum_{i=0}^{n+1} \begin{Bmatrix}n+1\\ i\end{Bmatrix}(-1)^{n+1-i}x^{\overline i}$$ ==== 性质三 ==== $$x^n=\sum_{i=0}^n \begin{Bmatrix}n\\ i\end{Bmatrix}x^{\underline i}$$ 其中 $x^{\underline i}$ 表示下降幂。 考虑归纳证明,有 $$x^{n+1}=x\sum_{i=0}^n \begin{Bmatrix}n\\ i\end{Bmatrix}x^{\underline i}=\sum_{i=0}^n \begin{Bmatrix}n\\ i\end{Bmatrix}\left(x^{\underline {i+1}}+ix^{\underline i}\right)=\sum_{i=0}^{n+1} \begin{Bmatrix}n+1\\ i\end{Bmatrix}x^{\underline i}$$ ==== 运算 ==== === 第二类斯特林数$\cdot$行 === [[https://www.luogu.com.cn/problem/P5395|洛谷p5395]] $$\begin{Bmatrix}n\\ i\end{Bmatrix}(0\le i\le n)$$ 考虑将 $n$ 个数装入 $k$ 个盒子,有 $k^n$ 种放法。其中 $i$ 个盒子中有球的方案数为 $i!{k\choose i}\begin{Bmatrix}n\\ i\end{Bmatrix}$ 于是 $k^n=\sum_{i=0}^k i!{k\choose i}\begin{Bmatrix}n\\ i\end{Bmatrix}$。设 $f(i)=i^n,g(i)=i!\begin{Bmatrix}n\\ i\end{Bmatrix}$,于是有 $f(k)=\sum_{i=0}^k {k\choose i}g(i)$。根据二项式反演 $$k!\begin{Bmatrix}n\\ k\end{Bmatrix}=\sum_{i=0}^k (-1)^{k-i}{k\choose i}i^n=k!\sum_{i=0}^k \cfrac{(-1)^{k-i}}{(k-i)!}\cfrac{i^n}{i!}$$ 于是 $O(n\log n)$ 卷积即可。 const int MAXN=2e5+5,Mod=167772161,G=3; int quick_pow(int a,int b){ int ans=1; while(b){ if(b&1) ans=1LL*ans*a%Mod; a=1LL*a*a%Mod; b>>=1; } return ans; } namespace Poly{ const int G=3; int rev[MAXN<<2],Wn[30][2]; void init(){ int m=Mod-1,lg2=0; while(m%2==0)m>>=1,lg2++; Wn[lg2][1]=quick_pow(G,m); Wn[lg2][0]=quick_pow(Wn[lg2][1],Mod-2); while(lg2){ m<<=1,lg2--; Wn[lg2][0]=1LL*Wn[lg2+1][0]*Wn[lg2+1][0]%Mod; Wn[lg2][1]=1LL*Wn[lg2+1][1]*Wn[lg2+1][1]%Mod; } } int build(int k){ int n,pos=0; while((1<>1]>>1)|((i&1)<<(pos-1)); return n; } void NTT(int *f,int n,bool type){ _for(i,0,n)if(i === 第二类斯特林数$\cdot$列 === [[https://www.luogu.com.cn/problem/P5409|洛谷p5396]] $$\begin{Bmatrix}i\\ k\end{Bmatrix}(0\le i\le n)$$ 考虑只有一个非空集合的方案的 $\text{EGF}$,有 $F(x)=\sum_{i=1}^{\infty} \cfrac {x^i}{i!}$。 于是 $k$ 个非空集合的 $\text{EGF}$ 为 $\cfrac {F^k(x)}{k!}$,利用 $\text{Exp}$ 和 $\text{Ln}$ 可以 $O(n\log n)$ 计算。 const int MAXN=1<<16,Mod=167772161,G=3; int quick_pow(int a,int b){ int ans=1; while(b){ if(b&1) ans=1LL*ans*a%Mod; a=1LL*a*a%Mod; b>>=1; } return ans; } namespace Poly{ const int G=3; int rev[MAXN<<2],Wn[30][2]; void init(){ int m=Mod-1,lg2=0; while(m%2==0)m>>=1,lg2++; Wn[lg2][1]=quick_pow(G,m); Wn[lg2][0]=quick_pow(Wn[lg2][1],Mod-2); while(lg2){ m<<=1,lg2--; Wn[lg2][0]=1LL*Wn[lg2+1][0]*Wn[lg2+1][0]%Mod; Wn[lg2][1]=1LL*Wn[lg2+1][1]*Wn[lg2+1][1]%Mod; } } int build(int k){ int n,pos=0; while((1<>1]>>1)|((i&1)<<(pos-1)); return n; } void NTT(int *f,int n,bool type){ _for(i,0,n)if(i>1); int n=build((_n-1)<<1); _for(i,(_n+1)>>1,n)g[i]=0; _for(i,0,_n)temp[i]=f[i];_for(i,_n,n)temp[i]=0; NTT(g,n,true);NTT(temp,n,true); _for(i,0,n)g[i]=(2-1LL*temp[i]*g[i]%Mod)*g[i]%Mod; NTT(g,n,false); _for(i,_n,n)g[i]=0; } void Ln(const int *f,int *g,int _n){ static int temp[MAXN<<2]; Inv(f,g,_n); _for(i,1,_n)temp[i-1]=1LL*f[i]*i%Mod; temp[_n-1]=0; Mul(g,_n,temp,_n-1,_n); for(int i=_n-1;i;i--)g[i]=1LL*g[i-1]*quick_pow(i,Mod-2)%Mod; g[0]=0; } void Exp(const int *f,int *g,int _n){ static int temp[MAXN<<2]; if(_n==1)return g[0]=1,void(); Exp(f,g,(_n+1)>>1); _for(i,(_n+1)>>1,_n)g[i]=0; Ln(g,temp,_n); temp[0]=(1+f[0]-temp[0])%Mod; _for(i,1,_n)temp[i]=(f[i]-temp[i])%Mod; Mul(g,(_n+1)>>1,temp,_n,_n); } void Pow(const int *f,int *g,int _n,int k1,int k2){ static int temp[MAXN<<2]; int pos=0,posv; while(!f[pos]&&pos<_n)pos++; if(1LL*pos*k2>=_n){ _for(i,0,_n)g[i]=0; return; } posv=quick_pow(f[pos],Mod-2); _for(i,pos,_n)g[i-pos]=1LL*f[i]*posv%Mod; _for(i,_n-pos,_n)g[i]=0; Ln(g,temp,_n); _for(i,0,_n)temp[i]=1LL*temp[i]*k1%Mod; Exp(temp,g,_n); pos=pos*k2,posv=quick_pow(posv,1LL*k2*(Mod-2)%(Mod-1)); for(int i=_n-1;i>=pos;i--)g[i]=1LL*g[i-pos]*posv%Mod; _for(i,0,pos)g[i]=0; } } int f[MAXN<<2],g[MAXN<<2],frac[MAXN<<1],invfrac[MAXN<<1]; int main() { Poly::init(); int n=read_int(),k=read_int(); frac[0]=1; _rep(i,1,n)frac[i]=1LL*frac[i-1]*i%Mod; invfrac[n]=quick_pow(frac[n],Mod-2); for(int i=n;i;i--)invfrac[i-1]=1LL*invfrac[i]*i%Mod; _rep(i,1,n)f[i]=invfrac[i]; Poly::Pow(f,g,n+1,k,k); _rep(i,0,n)g[i]=1LL*g[i]*frac[i]%Mod*invfrac[k]%Mod; _rep(i,0,n)space(g[i]); return 0; } ===== 练习题 ===== ==== 习题一 ==== [[https://www.luogu.com.cn/problem/P4091|洛谷p4091]] === 题意 === $$\sum_{i=0}^n\sum_{j=0}^i\begin{Bmatrix}i\\ j\end{Bmatrix}\times 2^j\times j!$$ === 题解 === 首先 $\begin{Bmatrix}i\\ j\end{Bmatrix}=0(j\gt i)$,于是 $$\sum_{i=0}^n\sum_{j=0}^i\begin{Bmatrix}i\\ j\end{Bmatrix}\times 2^j\times j!=\sum_{i=0}^n\sum_{j=0}^n\begin{Bmatrix}i\\ j\end{Bmatrix}\times 2^j\times j!=\sum_{j=0}^n2^j\times j!\sum_{i=0}^n\begin{Bmatrix}i\\ j\end{Bmatrix}$$ 直接展开第二类斯特林数,有 $$\sum_{j=0}^n2^j\times j!\sum_{i=0}^n\begin{Bmatrix}i\\ j\end{Bmatrix}=\sum_{j=0}^n2^j\times j!\sum_{i=0}^n\sum_{k=0}^j \frac{(-1)^{j-k}}{(j-k)!}\frac{k^i}{k!}=\sum_{j=0}^n2^j\times j!\sum_{k=0}^j \frac{(-1)^{j-k}}{(j-k)!}\frac{k^{n+1}-1}{k!(k-1)}$$ 需要注意 $k=0$ 时 $\cfrac{k^{n+1}-1}{k!(k-1)}=0$;$k=1$ 时 $\cfrac{k^{n+1}-1}{k!(k-1)}=n+1$。直接卷积即可,总时间复杂度 $O(n\log n)$。