【学习笔记】斯特林数

第一类斯特林数

定义:(S_1(n,m))表示(n)个元素,排成(m)个轮换的方案数。

标记:$egin{bmatrix} n mend{bmatrix} $

递推式:
(egin{bmatrix} n \ mend{bmatrix} = (n-1)egin{bmatrix} n-1 \ mend{bmatrix} +egin{bmatrix} n-1 \ m-1end{bmatrix})
通过组合意义理解就是新加入一个元素可以放入任何一个元素之前或者新开一个轮换

可能会用到的柿子:(n! = sum_{i=0}^n egin{bmatrix} n \ iend{bmatrix})显然,一个排列唯一对应一组轮换

可以分治FFT在(O(nlg^2n))的时间里求出固定的(n)对应的第一类斯特林数

第二类斯特林数

定义:(S_2(n,m))表示(n)个元素,组成(m)个集合的方案数。

标记:(egin{Bmatrix} n \ mend{Bmatrix})

递推式:
(egin{Bmatrix} n \ mend{Bmatrix} = megin{Bmatrix} n-1 \ mend{Bmatrix}+egin{Bmatrix} n-1 \ m-1end{Bmatrix})
分别对应加入之前的集合新开一个集合

可能会用到的柿子:(egin{Bmatrix} n \ mend{Bmatrix} = frac{1}{m!} sum_{k=0}^m (-1)^k inom{m}{k} (m-k)^n)

对应容斥原理,比之前方便,直接卷积就能求了

(n^k = sum_{i=0}^k egin{Bmatrix} k \ iend{Bmatrix} i! inom{n}{i})

貌似要下降幂推导 我还是没学明白下降幂 先鸽着好了/cy

斯特林数也可以做反演...长这样

(q_n = sum_{i=1}^n egin{Bmatrix} n \ iend{Bmatrix} p_i Leftrightarrow p_n = sum_{i=0}^n (-1)^{n-i} egin{bmatrix} n \ iend{bmatrix} q_i)

例题:TJOI/HEOI2016 求和

我们一起来推柿子啦

首先由于(i>j)(S(i,j)=0)所以我们的求和式可以直接简化一下

(sum_{i=0}^n sum_{j=0}^i S(i,j) 2^j j! = sum_{i=0}^n sum_{j=0}^n S(i,j) 2^j j!)

好看多了 我们直接开始推吧 直接把定义式扔进去

(sum_{j=0}^n 2^j j! frac{1}{j!}sum_{i=0}^n sum_{k=0}^j inom{j}{k}(-1)^k (j-k)^i frac{1}{k!}\ = sum_{j=0}^n 2^j sum_{i=0}^n frac{j!}{k!(j-k)!} (-1)^k (j-k)^i \ = sum_{j=0}^n 2^j j! sum_{k=0}^j sum_{i=0}^n frac{(-1)^k}{k!} frac{(j-k)^i}{(j-k)!}\= sum_{j=0}^n 2^j j! sum_{k=0}^j frac{(-1)^k}{k!} frac{(j-k)^{n+1} - 1}{(j-k)!(j-k-1)})

显然后面长得就像个卷积

然后直接NTT就好辣

(呜呜明明我是为了分治NTT来做的这个题 它偏偏不用)

//Love and Freedom.
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define inf 20021225
#define mdn 998244353
#define G 3
#define N 300010
using namespace std;
int read()
{
	int s=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return f*s;
}
int ksm(int bs,int mi)
{
	int ans=1;
	while(mi)
	{
		if(mi&1)	ans=1ll*ans*bs%mdn;
		bs=1ll*bs*bs%mdn; mi>>=1;
	}
	return ans;
}
int r[N];
int init(int n)
{
	int lim=1,l=0;
	while(lim<n)	lim<<=1,l++;
	for(int i=0;i<lim;i++)	r[i]=(r[i>>1]>>1)|((i&1)<<l-1);
	return lim;
}
void ntt(int *a,int lim,int f)
{
	for(int i=0;i<lim;i++)	if(r[i]>i)	swap(a[r[i]],a[i]);
	for(int k=2,mid=1;k<=lim;mid<<=1,k<<=1)
	{
		int Wn=ksm(G,(mdn-1)/k); if(f)	Wn=ksm(Wn,mdn-2);
		for(int i=0,w=1;i<lim;i+=k,w=1)	for(int j=0;j<mid;j++,w=1ll*w*Wn%mdn)
		{
			int x=a[i+j],y=1ll*w*a[i+mid+j]%mdn;
			a[i+j]=(x+y)%mdn; a[i+mid+j]=(x-y+mdn)%mdn;
		}
	}
	if(f)	for(int i=0,inv=ksm(lim,mdn-2);i<lim;i++)	a[i]=1ll*a[i]*inv%mdn;
}
int f[N],g[N],fac[N],inv[N];
int main()
{
	int n=read(); fac[0]=1;
	for(int i=1;i<=n;i++)	fac[i]=1ll*fac[i-1]*i%mdn;
	inv[n]=ksm(fac[n],mdn-2);
	for(int i=n;i;i--)	inv[i-1]=1ll*inv[i]*i%mdn;
	f[0]=g[0]=1; f[1]=n+1; g[1]=mdn-1;
	for(int i=2;i<=n;i++)	f[i]=1ll*(ksm(i,n+1)-1+mdn)%mdn*inv[i]%mdn*ksm((i-1+mdn)%mdn,mdn-2)%mdn;
	for(int i=2;i<=n;i++)	g[i]=i&1?mdn-inv[i]:inv[i];
	int lim=init(n+1<<1); ntt(g,lim,0); ntt(f,lim,0);
	for(int i=0;i<lim;i++)	f[i]=1ll*f[i]*g[i]%mdn;
	ntt(f,lim,1); int ans=0;
	for(int i=0;i<=n;i++)	(ans+=1ll*ksm(2,i)*fac[i]%mdn*f[i]%mdn)%=mdn;
	printf("%d
",ans);
	return 0;
}

原文地址:https://www.cnblogs.com/hanyuweining/p/12050992.html