斯特林数的四种求法

斯特林数的四种求法

一、第一类斯特林数求行

因为

\[x^{\overline{n}}=\sum_{i=0}^{n}\left[\begin{matrix}n\\i\end{matrix}\right]x^i \]

所以只要求出\(x^{\overline{n}}\)的系数就能求出第一类斯特林数的一行,那么如何求解\(x^{\overline{n}}\)呢?

注意到\(x^{\overline{2n}}=x^{\overline{n}}(x+n)^{\overline{n}}\),因此只要我们能够在已知\(f(x)\)的情况下快速求解\(f(x+n)\),那么就可以通过\(\mathcal O(nlog(n))\)完成。

不妨设

\[f(x)=\sum_{i=0}^{n}a_ix^i \]

那么

\[\begin{aligned} f(x+c)&=\sum_{i=0}^{n}a_i(x+c)^i\\ &=\sum_{i=0}^{n}a_i\sum_{j=0}^{i}\binom ijx^jc^{i-j}\\ &=\sum_{j=0}^{n}x^j\sum_{i=j}^{n}a_i\binom ij c^{i-j}\\ &=\sum_{j=0}^{n}\dfrac{x^j}{j!}\sum_{i=j}^{n}a_ii!\dfrac{c^{i-j}}{(i-j)!} \end{aligned} \]

后面是一个减法卷积的形式了,于是可以\(\mathcal O(nlog(n))\)快速求解。

#include "poly.cpp"

inline poly trans(poly f,int n,int c){
	init_inv(n);
	poly g;int t=1;
	for(int i=0;i<n;t=1ll*t*c%mod,++i) f[i]=1ll*base[i]*f[i]%mod,g[i]=1ll*t*jc[i]%mod;
	reverse(f.v.begin(),f.v.begin()+n);
	poly h=Basic::mul(f,g,n,n),ret;
	for(int i=0;i<n;++i) ret[i]=1ll*h[n-1-i]*jc[i]%mod;
	return ret;
}
inline poly solve(int l,int r){
	if(l>r) return poly(1);
	if(l==r){poly g;g[1]=1;g[0]=l;return g;}
	int len=r-l+1,mid=(l+r)>>1;
	if(len&1) return Basic::mul(solve(l,mid),solve(mid+1,r),mid-l+2,r-mid+1);
	else{
		poly g=solve(l,mid),h=trans(g,mid-l+2,len/2);
		return Basic::mul(g,h,mid-l+2,r-mid+1);
	}
}
int main(){
//	freopen("poly.in","r",stdin);
	int n=Rint;
	init_poly((n+1)<<1);
	solve(0,n-1).print(n+1);
	IO::flush();
	return 0;
}

二、第二类斯特林数求行

利用通项公式直接求解即可。

\[\left\{\begin{matrix}n\\m\end{matrix}\right\}=\dfrac{1}{m!}\sum_{k=0}^{m}(-1)^k\binom{m}{k}(m-k)^n \]

#include "poly.cpp"

int n,m;
poly f,g;
int main(){
//	freopen("poly.in","r",stdin);
	n=Rint;
	init_poly((n+1)<<1);
	init_inv(n+1);
	for(int i=0;i<=n;++i){
		f[i]=1ll*jc[i]*(i&1?mod-1:1)%mod;
		g[i]=1ll*ksm(i,n)*jc[i]%mod;
	}
	f=Basic::mul(f,g,n+1,n+1);f.print(n+1);
	IO::flush();
	return 0;
} 

三、第一类斯特林数求列

一个环的EGF为

\[F(x)=\sum_{i=1} \dfrac{(i-1)!}{i!}x^i=\ln(\frac{1}{1-x}) \]

于是答案的EGF就是:

\[\frac{F(x)^k}{k!} \]

\(x!\)是因为集合是无序的,而\(k\)次方把它当成了有序。

#include "poly.cpp"

int n,k,base[N],ivk;
poly f;
int main(){
	scanf("%d%d",&n,&k);
	if(n<k){for(int i=0;i<=n;++i) pc('0'),pc(' ');IO::flush();return 0;}
	init_inv(n+1);
	init_poly(n+1);
	base[0]=1;
	for(int i=1;i<=n;++i) base[i]=1ll*base[i-1]*i%mod;
	int ivk=ksm(base[k],mod-2);
	for(int i=1;i<=n;++i) f[i-1]=iv[i];
	f=Exp::getpow(f,n,k);
	for(int i=0;i<k;++i) pc('0'),pc(' ');
	for(int i=k,j=0;i<=n;++i,++j)
		put(1ll*f[j]*base[i]%mod*ivk%mod,' ');
	IO::flush(); 
	return 0;
}

四、第二类斯特林数求列

一个集合的EGF为:

\[\sum_{i=1}\frac{1}{i!}x^i=e^x-1 \]

于是同理多项式快速幂即可。

#include "poly.cpp"

int n,k,base[N],jc[N],ivk;
poly f;
int main(){
	scanf("%d%d",&n,&k);
	if(n<k){for(int i=0;i<=n;++i) pc('0'),pc(' ');IO::flush();return 0;}
	init_inv(n+1);
	init_poly(n+1);
	base[0]=1;jc[0]=1;
	for(int i=1;i<=n;++i) base[i]=1ll*base[i-1]*i%mod,jc[i]=1ll*jc[i-1]*iv[i]%mod;
	int ivk=ksm(base[k],mod-2);
	for(int i=1;i<=n;++i) f[i-1]=jc[i];
	f=Exp::getpow(f,n-k+1,k);
	for(int i=0;i<k;++i) pc('0'),pc(' ');
	for(int i=k,j=0;i<=n;++i,++j)
		put(1ll*f[j]*base[i]%mod*ivk%mod,' ');
	IO::flush(); 
	return 0;
}
posted @ 2021-03-29 17:12  cjTQX  阅读(535)  评论(0)    收藏  举报