斯特林数 & 贝尔数学习笔记

背景:

好久没有更 blog \text{blog} blog,最近都在准备模拟( )赛。



第一类斯特林数:

第一类斯特林数第 n n n m m m列,记做 [   n m ] \LARGE[_{\,n}^m] [nm]

que \text{que} que

题目传送门:https://www.luogu.org/problem/P5408
现在你有 n n n个珠子,每一个珠子的颜色各不相同,求这些珠子组成 m m m个项链的方案数。
现在求 1 , 2 , 3 , . . . m 1,2,3,...m 1,2,3,...m时方案数。

sol \text{sol} sol

S n , m S_{n,m} Sn,m表示上面的方案数。
那么有:
S n , m = S n − 1 , m − 1 + ( n − 1 ) S n − 1 , m S_{n,m}=S_{n-1,m-1}+(n-1)S_{n-1,m} Sn,m=Sn1,m1+(n1)Sn1,m

解释一下:对于当前的珠子来说,有两种选择:
[ 1 ] [1] [1]:新开一个项链,那么方案数就要继承 S n − 1 , m − 1 S_{n-1,m-1} Sn1,m1
[ 2 ] [2] [2]:在原来的项链里加入,考虑它会加在某一个珠子的后面,由于之前已经有 ( n − 1 ) (n-1) (n1)个珠子,可以加在任意一个后面,因此方案数为 ( n − 1 ) S n − 1 , m (n-1)S_{n-1,m} (n1)Sn1,m

可是这是 Θ ( n m ) \Theta(nm) Θ(nm)的大暴力啊。
第一类斯特林数有一个性质就是它可以表示下面的形式:
S n , m = [ x m ] x ( x + 1 ) ( x + 2 ) + . . . + ( x + n − 1 ) S_{n,m}=[x^m]x(x+1)(x+2)+...+(x+n-1) Sn,m=[xm]x(x+1)(x+2)+...+(x+n1)

现在这个式子就可以分治 NTT \text{NTT} NTT解决了。
时间复杂度: Θ ( n log ⁡ 2 n ) \Theta(n\log^2n) Θ(nlog2n)
而模板题卡这种做法,因此不能通过。

Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)的做法,可我太菜了,不想学。

code \text{code} code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const LL mod=167772161,G=3,inv_G=55924054;
using namespace std;
	int r[800010],f[800010],a[20][800010],b[20][800010];
	int n,A,B,l,limit;
int dg(int x,int k)
{
	if(!k) return 1;
	int op=dg(x,k>>1);
	if(k&1) return (LL)op*op%mod*x%mod; else return (LL)op*op%mod;
}
int get_inv(int x)
{
	return dg(x,mod-2);
}
void init(int n)
{
	limit=1,l=0;
	while(limit<(n<<1))
		limit<<=1,l++;
	for(int i=1;i<limit;i++)
		r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(int *now,int limit,int op)
{
	for(int i=0;i<limit;i++)
		if(i<r[i]) swap(now[i],now[r[i]]);
	for(int mid=1;mid<limit;mid<<=1)
	{
		int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
		for(int j=0;j<limit;j+=(mid<<1))
		{
			int w=1;
			for(int k=0;k<mid;k++,w=((LL)w*wn)%mod)
			{
				int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
				now[j+k]=(x+y)%mod;
				now[j+k+mid]=(x-y+mod)%mod;
			}
		}
	}
	if(op==1) return;
	int INV=get_inv(limit);
	for(int i=0;i<limit;i++)
		now[i]=(LL)now[i]*INV%mod;
}
void poly_CDQ(int *f,int dep,int l,int r)
{
	if(l==r)
	{
		f[0]=l;f[1]=1;
		return;
	}
	int mid=(l+r)>>1;
	init(r-l+1+1);
	for(int i=0;i<limit;i++)
		a[dep][i]=b[dep][i]=0;
	poly_CDQ(a[dep],dep+1,l,mid),poly_CDQ(b[dep],dep+1,mid+1,r);
	init(r-l+1+1);
	NTT(a[dep],limit,1),NTT(b[dep],limit,1);
	for(int i=0;i<limit;i++)
		f[i]=(LL)a[dep][i]*b[dep][i]%mod;
	NTT(f,limit,-1);
}
int main()
{
	scanf("%d",&n);
	poly_CDQ(f,0,0,n-1);
	for(int i=0;i<=n;i++)
		printf("%d ",f[i]);
}


第二类斯特林数:

第二类斯特林数第 n n n m m m列,记做 { &ThinSpace; n m } \left\{{\LARGE_{\,n}^m}\right\} {nm}
这一个更加广泛应用。

que \text{que} que

题目传送门:https://www.luogu.org/problem/P5395
现在你有 n n n个珠子,每一个珠子的颜色各不相同,求这些珠子放进 m m m个盒子的方案数。
现在求 1 , 2 , 3 , . . . m 1,2,3,...m 1,2,3,...m时方案数。

sol \text{sol} sol

S n , m S_{n,m} Sn,m表示上面的方案数。
那么有:
S n , m = S n − 1 , m − 1 + m S n − 1 , m S_{n,m}=S_{n-1,m-1}+mS_{n-1,m} Sn,m=Sn1,m1+mSn1,m

解释一下:对于当前的珠子来说,有两种选择:
[ 1 ] [1] [1]:新开一个盒子,那么方案数就要继承 S n − 1 , m − 1 S_{n-1,m-1} Sn1,m1
[ 2 ] [2] [2]:在原来的盒子里加入,考虑它会加在某一个盒子里,由于之前已经有 m m m个盒子,可以加在任意一个里面,因此方案数为 ( n − 1 ) S n − 1 , m (n-1)S_{n-1,m} (n1)Sn1,m

可是这是 n m nm nm的大暴力啊。
第二类斯特林数有一个性质就是它可以表示下面的形式:
S n , m = 1 m ! ∑ k = 0 m ( − 1 ) k C m k ( m − k ) n S_{n,m}=\frac{1}{m!}\sum_{k=0}^{m}(-1)^kC_{m}^{k}(m-k)^n Sn,m=m!1k=0m(1)kCmk(mk)n

S n , m = 1 m ! ∑ k = 0 m ( − 1 ) k m ! k ! ( m − k ) ! ( m − k ) n S_{n,m}=\frac{1}{m!}\sum_{k=0}^{m}(-1)^k\frac{m!}{k!(m-k)!}(m-k)^n Sn,m=m!1k=0m(1)kk!(mk)!m!(mk)n

S n , m = ∑ k = 0 m ( − 1 ) k 1 k ! ( m − k ) ! ( m − k ) n S_{n,m}=\sum_{k=0}^{m}(-1)^k\frac{1}{k!(m-k)!}(m-k)^n Sn,m=k=0m(1)kk!(mk)!1(mk)n

S n , m = ∑ k = 0 m ( − 1 ) k k ! ( m − k ) n ( m − k ) ! S_{n,m}=\sum_{k=0}^{m}\frac{(-1)^k}{k!}\frac{(m-k)^n}{(m-k)!} Sn,m=k=0mk!(1)k(mk)!(mk)n

为什么呢?
假设每一个盒子都不同,并且我们允许空盒的存在。
但是我们当然就是不允许空盒存在啊。
所以考虑容斥。
枚举我当前有 k k k个空盒子。
那么先把这 k k k个盒子选出来,也就是 C m k C_{m}^{k} Cmk
然后剩下 m − k m−k mk个盒子, n n n个球可以随便放,也就是 ( m − k ) n (m−k)^n (mk)n
考虑到我们盒子是没有区别的,所以除以 m ! m! m!

现在这个式子是一个卷积的形式,就可以 NTT \text{NTT} NTT解决了。
时间复杂度: Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)

code \text{code} code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const int mod=167772161,G=3,inv_G=55924054;
using namespace std;
	int a[1000010],b[1000010],f[1000010],g[1000010],inv[1000010],Inv[1000010];
	int limit,n,l,r[1000010];
int dg(int x,int k)
{
	if(!k) return 1;
	int op=dg(x,k>>1);
	if(k&1) return (LL)op*op%mod*x%mod; else return (LL)op*op%mod;
}
int get_inv(int x)
{
	return dg(x,mod-2);
}
void init(int n)
{
	limit=1,l=0;
	while(limit<(n<<1))
		limit<<=1,l++;
	for(int i=1;i<limit;i++)
		r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(int *now,int limit,int op)
{
	for(int i=0;i<limit;i++)
		if(i<r[i]) swap(now[i],now[r[i]]);
	for(int mid=1;mid<limit;mid<<=1)
	{
		int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
		for(int j=0;j<limit;j+=(mid<<1))
		{
			int w=1;
			for(int k=0;k<mid;k++,w=((LL)w*wn)%mod)
			{
				int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
				now[j+k]=(x+y)%mod;
				now[j+k+mid]=(x-y+mod)%mod;
			}
		}
	}
}
void dft(int *f,int n,int limit)
{
	NTT(f,limit,-1);
	int INV=get_inv(limit);
	for(int i=0;i<n;i++)
		f[i]=(LL)f[i]*INV%mod;
}
void Init()
{
	inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++)
		inv[i]=((LL)mod-mod/i)*inv[mod%i]%mod;

	Inv[0]=Inv[1]=1;
	for(int i=2;i<=n;i++)
		Inv[i]=(LL)Inv[i-1]*inv[i]%mod;
}
int main()
{
	scanf("%d",&n);
	Init();
	for(int i=0;i<=n;i++)
	{
		f[i]=(((i&1)?-1ll:1ll)*Inv[i]+mod)%mod;
		g[i]=(LL)dg(i,n)*Inv[i]%mod;
	}
	init(n+1);
	NTT(f,limit,1),NTT(g,limit,1);
	for(int i=0;i<limit;i++)
		f[i]=(LL)f[i]*g[i]%mod;
	dft(f,n+1,limit);
	for(int i=0;i<=n;i++)
		printf("%d ",f[i]);
}


贝尔数:

que \text{que} que

现在你有 n n n个珠子,每一个珠子的颜色各不相同,求这些珠子放进若干个盒子的方案数。
现在求 1 , 2 , 3 , . . . m 1,2,3,...m 1,2,3,...m时方案数。

sol \text{sol} sol

枚举盒子数 i i i,显然我们有:
B n , m = ∑ i = 1 m { n &ThinSpace; i } B_{n,m}=\sum_{i=1}^{m}\left\{{\LARGE_{n}^{\,i}}\right\} Bn,m=i=1m{ni}

可是这样的时间复杂度是 Θ ( n m ) \Theta(nm) Θ(nm)的,而且还要预处理第二类斯特林数,极其复杂。
考虑它的递推式:
B n = ∑ k = 1 n − 1 C n − 1 k B k B_{n}=\sum_{k=1}^{n-1}C_{n-1}^{k}B_k Bn=k=1n1Cn1kBk

理解起来也不难,枚举珠子数 k k k,考虑从 n − 1 n-1 n1个珠子里选出 k k k个珠子放在一个盒子里,那么
未完待续。



后记:

有什么好的套路或性质以后再补充吧,毕竟还没有做题。
欢迎大佬指出错误。
一些性质:
n k = ∑ i = 0 k { k &ThinSpace; i } C n i i ! n^k=\sum_{i=0}^{k}\left\{{\LARGE_{k}^{\,i}}\right\}C_{n}^{i}i! nk=i=0k{ki}Cnii!

持续更哦。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值