组合数学学习笔记(四):特殊的数

卡塔兰数(Catalan

斯特林数(Stirling

斯特林数作为组合数学中非常重要的一类数,一共分为第一类斯特林数与第二类斯特林数,在处理复杂的小球与盒子的关系时有重要的作用。我们先从比较简单的第二类斯特林数讲起。

第二类斯特林数

定义

{nk} 表示将 n 个元素划分成 k 个非空子集的方案数,{nk} 就叫做第二类斯特林数。

n 较小时的第二类斯特林数如下:

第二类斯特林数有一些特殊值:

  • {n0}=[n=0]:将非零个元素划分为 0 个集合不存在,将 0 个元素划分为 0 个集合算一种情况;

  • {n1}={nn}=1,将 n 个元素划分为 1 个集合,每个元素只能放入这一个集合中;将 n 个元素划分为 n 个集合,每个元素只能单独成一个集合;

  • {n2}=2n11,考虑先将 1 划分到一个集合,再考虑其它 n1 个数是放在有 1 的这个集合,还是没 1 的这个集合,最后减去全放在有 1 的这个集合中的 1 种情况;

  • {nn1}=(n2),考虑到此时只有 1 个集合大小为 2,枚举一下是哪个集合即可。

递推公式与通项公式

递推公式

{nk}=k{n1k}+{n1k1}

即考虑第 n 个元素放在哪个集合。要么在已经有的 k 个非空集合里选一个放进去,要么自己单开一个新的集合。

通项公式

{nk}=1k!i=0k(1)i(ki)(ki)n=i=0k(1)kiini!(ki)!

证明:设将 n 个有标号物品放到 i 个有标号盒子(允许为空)的方案数为 Gi;将 n 个有标号物品放到 i 个有标号盒子(不允许为空)的方案数为 Fi

对于 Gi 的求解是简单的,考虑每个物品放入的是哪个盒子,因此 Gi=in。考虑钦定哪几个盒子非空,于是有 Gi=j=0i(ij)Fj

二项式反演一下,可以得到 Fi=j=0i(1)ij(ij)Gi,因此 Fk=i=0k(1)ki(ki)in=i=0kk!(1)kiini!(ki)!

由于 Fk 是有标号的盒子,而 {nk} 是无标号的盒子,因此将式子再除以 k! 得到 {nk}=i=0k(1)kiini!(ki)!

生成函数

应用

高阶差分

普通幂转下降幂

xn 可以写成一个 n 次的多项式 i=0n1(xi),因此 xn 一定可以由若干下降幂来表示,计算发现:

x0=x0x1=x1x2=x2+x1x3=x3+3x2+x1x4=x4+6x3+7x2+x1

可以发现它的系数正好是第二类斯特林数,因此可以得出结论:xn=k=0n{nk}xk=k=0n{nk}(nk)k!

证明:

考虑数学归纳法,当 n=0 时我们已经证明了这个等式是正确的。现在假设对于 0n1,该等式都成立,现在要证明该等式对于 n 成立。

首先,我们知道 xk+1=xk(xk),于是 x×xk=xk+1+kxk

那么

xn=x×xn1=x×k=0n1{n1k}xk=k=0n1{n1k}xk+1+k=0n1{n1k}kxk

考虑到 {n10}0x0={n1n}nxn=0,因此可以随意加减在加号右边,因此原式

=k=1n{n1k1}xk+k=1n{n1k}kxk=k=1n({n1k1}+k{n1k})xk=k=0n{nk}xk

即,我们可以将第二类斯特林数看成,由普通幂转为下降幂时,产生的系数。

普通幂转上升幂

类似下降幂来定义上升幂为 xn=x(x+1)(x+2)(x+n1)=i=0n1(x+i)

我们将 x 带入 k=0n{nk}xk 得到 (1)nxn=k=0n{nk}(x)k,而 xn=(1)n(x)n,于是可以得到 xn=k=0n(1)nk{nk}xk

第一类斯特林数

定义

[nk] 表示将 n 个元素划分成 k 个圆排列的方案数,[nk] 就叫做第一类斯特林数。

圆排列是指将一个排列围成一个圈,如果两个圆排列旋转之后一样,那么这两个圆排列就是相同的,比如 [A,B,C,D]=[B,C,D,A]=[C,D,A,B]=[D,A,B,C][x1,x2,,xn] 表示从竖直方向顺时针依次填入 x1,x2,,xn,最后又回到竖直方向上所形成的圆排列)。

n 较小时的第一类斯特林数如下:

第一类斯特林数有一些特殊值:

  • [n0]=[n=0]:将非零个元素划分为 0 个圆排列不存在,将 0 个元素划分为 0 个圆排列算一种情况;

  • [nn]=1(n>0):将 n 个元素划分为 n 个圆排列,每个元素只能单独成一个圆排列;

  • [n1]=(n1)!(n>0):考虑到普通排列的数量有 n! 个,每个圆排列对应 n 个普通排列,于是圆排列的数量就变成了 n!n=(n1)!

  • [nn1]=(n2),考虑到此时只有 1 个圆排列大小为 2,枚举一下是哪个圆排列即可。

可以发现,在第二类斯特林数分成的 k 个集合中,第一类斯特林数还要考虑顺序,因此 [nk]{nk}

递推公式

[nk]=(n1)[n1k]+[n1k1]

即考虑第 n 个元素是插入这 k 个圆排列中的哪个位置,也就是考虑是将那个圆排列中的哪条边断掉,将第 n 个元素插入再接上,可以发现一共有 n1 条边可以断掉再插入,因此一共有 (n1)[n1k] 种选择:

由于第 n 个元素还能自成一个圆排列,因此 [nk]=(n1)[n1k]+[n1k1]

可惜的是,第一类斯特林数没有实用的通项公式 (虽然第二类斯特林数的通项公式也不实用)

生成函数

应用

上升幂转普通幂

xn 可以写成一个 n 次的多项式 i=0n1(x+i),可以发现这也是一个关于 xn 次多项式,因此 xn 一定可以由若干普通幂来表示,计算发现:

x0=x0x1=x1x2=x2+x1x3=x3+3x2+2xx4=x4+6x3+11x2+6x

可以发现它的系数正好是第一类斯特林数,因此可以得出结论:xn=k=0n[nk]xk

证明:

还是考虑数学归纳法,当 n=0 时我们已经证明了这个等式是正确的。现在假设对于 0n1,该等式都成立,现在要证明该等式对于 n 成立。

这次,我们要知道 (x+n1)k×xk=xk+1+(n1)xk

那么

xn=(x+n1)xn1=(x+n1)k=0n1[n1k]xk=k=0n1[n1k]xk+1+k=0n1(n1)[n1k]xk

一样的道理,[n10](n1)x0=[n1n](n1)xn=0,因此可以随意加减在加号右边,因此原式

=k=1n[n1k1]xk+k=1n(n1)[n1k]xk=k=1n[(n1)[n1k]+[n1k1]]xk=k=0n[nk]xk

即,我们可以将第一类斯特林数看成,从上升幂转普通幂时,产生的常数。

下降幂转普通幂

考虑到上升幂和下降幂奇数次方项的系数正好相反:

x4=x4+6x3+11x2+6xx4=x46x3+11x26x

因为 xn=k=0n[nk]xk,所以 xn=k=0n(1)nk[nk]xk


例题一

例题二

解:首先把 f(x) 写成下降幂多项式 i=0maiik=i=0maij=0i{ij}kj=i=0m(j=im{ji}aj)ki(交换求和顺序后再将 ij 的意义互换),发现内部的 j=im{ji}aj 是好求的,将其设为 bi,那么问题变成了求 k=0ni=0mbiki×xk×(nk)

由于 (nk)×ki=(nk)(ki)i!=(ni)i!(niki)=ni(niki),那么原式 =i=0mbinik=0n(niki)xk=i=0mbinixik=0ni(nik)xk

可以发现里面是一个二项式定理的形式,于是原式最终 =i=0mbinixi(x+1)ni,于是用 O(m2) 的时间复杂度解决了此题。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e3 + 9;
int s[N][N], d[N], a[N], b[N], n, x, p, m;
int qpow(int a, int b){
	int res = 1;
	while(b > 0){
		if(b & 1)
			res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}
signed main(){
	scanf("%lld%lld%lld%lld", &n, &x, &p, &m);
	s[0][0] = 1;
	for(int i = 1; i < N; i++)
		for(int j = 1; j < N; j++)
			s[i][j] = (j * s[i - 1][j] + s[i - 1][j - 1]) % p;
	for(int i = 0; i <= m; i++)
		scanf("%lld", &a[i]);
	for(int i = 0; i <= m; i++)
		for(int j = i; j <= m; j++)
			b[i] = (b[i] + s[j][i] * a[j]) % p;
	d[0] = 1;
	for(int i = n, j = 1; i >= (n - m + 1); i--, j++)
		d[j] = d[j - 1] * i % p;
	int ans = 0;
	for(int i = 0; i <= m; i++)
		ans = (ans + b[i] * d[i] % p * qpow(x, i) % p * qpow(x + 1, n - i) % p) % p;
	printf("%lld", ans);
	return 0;
}

例题三

这个建筑师建造的城市让我想起了 指环王 中的:

考虑把最大值作为分界点,左边的前缀最大值构成一个 A1 个台阶的阶梯,后缀最大值构成了一个 B1 个台阶的阶梯:

现在每个阶梯分成一个组,每个组内除了最大值必须在第一个,其它值可以随便排,考虑到高度是 1n 的排列,于是无论怎么分组,每组都有最大值,将每组最大值排序后,一定可以得到一个如图的阶梯。由于要考虑顺序,因此分组的方案数是 [n1A+B2]

现在考虑哪些值在左边的阶梯,哪些值在右边的阶梯,因此分左右的组合有 (A+B2A1) 种,最终答案为 [n1A+B2]×(A+B2A1)O(nm) 预处理斯特林数,O(m2) 预处理组合数就可以 O(T) 求出答案了。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e4 + 9, M = 2e2 + 9, MOD = 1e9 + 7;
int s[N][M], b[M][M], T, n, A, B;
signed main(){
	s[0][0] = 1;
	for(int i = 1; i < N; i++)
		for(int j = 1; j < M; j++)
			s[i][j] = ((i - 1) * s[i - 1][j] + s[i - 1][j - 1]) % MOD;
	for(int i = 0; i < M; i++)
		b[i][0] = 1;
	for(int i = 1; i < M; i++)
		for(int j = 1; j < M; j++)
			b[i][j] = (b[i - 1][j] + b[i - 1][j - 1]) % MOD;
	scanf("%lld", &T);
	while(T--){
		scanf("%lld%lld%lld", &n, &A, &B);
		printf("%lld\n", s[n - 1][A + B - 2] * b[A + B - 2][A - 1] % MOD);
	}
	return 0;
} 

例题四

例题五

给定 n,m,b,c,求满足下列条件的 m 元组 (x1,x2,x3,,xm) 的个数模 998244353

  • xi[0,bic]Z

  • xin

2b<108108c<b1m801nbm+1n 用高精度表示。

斯特林数恒等式

斯特林数与伯努利数的关系

我们现在学过的特殊的数有:组合数、卡塔兰数、伯努利数、斯特林数,其中伯努利数与斯特林数有一些密切的关系。

第二类斯特林数求行

第二类斯特林数求列

第一类斯特林数求行

第一类斯特林数求列

斯特林反演

例题六

例题七

欧拉数(Eulerian

调和级数(Harmonic

伯努利数(Bernoulli

考虑自然数的幂的和 Sm(n)=0m+1m+2m++(n1)m=i=0n1im,我们来观察 m 取特殊值时该式子的变化:

S0(n)=nS1(n)=12n212nS2(n)=13n312n2+16nS3(n)=14n412n3+14n2S4(n)=15n512n4+13n3130nS5(n)=16n612n5+512n4112n2

可以发现系数之间存在一些关系。首先可以发现 Sm(n) 一定是一个 m+1 次多项式,它的 nm+1 项的系数一定是 1m+1nm 项的系数一定是 12nm1 项的系数一定是 m12nm2 项的系数一定是 0nm3 项的系数一定是 m(m1)(m2)720……。当 nm3 项的系数出现时,我们可以猜想 nmk 项的系数应该是某个常数乘以 mk,于是 Sm(n)=1m+1i=0m(m+1i)Binm+1i,其中的 Bi 就被称作伯努利数。

伯努利数隐含一个递归关系:j=0m(m+1j)Bj=[m=0],这会在后续用伯努利数的指数生成函数证明,此处可以先当结论记住。

伯努利数意义的证:考虑数学归纳法。

边界情况是好证的,同时我们也可以证明 B0=1

假设对于 0m1,都有 Si(n)=1m+1i=0m(m+1i)Binm+1i

首先,先进行一个简单的变换,那就是 Sm+1(n)+nm+1=i=0n1(i+1)m+1=i=0n1j=0m+1(m+1j)ij(用下指标一行之和展开)=j=0m+1(m+1j)Sj(n),于是两边同时减去 Sm+1(n),可以得到 nm+1=j=0m(m+1j)Sj(n)

现在考虑一个新的求和方法:扰动法。我们先设 S^m(n)=1m+1i=0m(m+1i)Binm+1i,于是我们想证明 Sm(n)=S^m(n),现在我们知道这对于 0m1 成立,假设在 m 作为指数时 Sm(n)S^m(n) 有差距 Δ,现在我们要证明 Δ=0

由于 nm+1=j=0m(m+1j)Sj(n),将最后一项提出,那么前面所有 Sm(n)=S^m(n),而最后一项与 S^m(n)Sm(n)Δ,于是 j=0m(m+1j)Sj(n)=j=0m(m+1j)S^j(n)+(m+1m)Δ=j=0m(m+1j)S^j(n)+(m+1)Δ

S^m(n) 展开,于是可以得到原式

=j=0m(m+1j)1j+1k=0j(j+1k)Bknj+1k+(m+1)Δ=k=0mj=km(m+1j)(j+1k)Bkj+1nj+1k+(m+1)Δ

考虑改变 k 的意义,变成 jk 的差值,于是原式

=k=0mj=km(m+1j)(j+1jk)Bjkj+1nk+1+(m+1)Δ=k=0mj=km(m+1j)(j+1k+1)Bjkj+1nk+1+(m+1)Δ=k=0mnk+1k+1j=km(m+1j)(jk)Bjk+(m+1)Δ

发现其中有一个三项式系数恒等,将其变换后再调整求和顺序可以得到原式

=k=0mnk+1k+1(m+1k)j=km(m+1kjk)Bjk+(m+1)Δ

再次改变 j 的意义为 jk 的差值,于是原式

=k=0mnk+1k+1(m+1k)j=0mk(m+1kj)Bj+(m+1)Δ

可以发现 j=0mk(m+1kj)Bj 是伯努利数的递归关系,于是原式

=k=0mnk+1k+1(m+1k)[mk=0]+(m+1)Δ=nm+1m+1(m+1m)+(m+1)Δ=nm+1+(m+1)Δ

现在我们就知道了 nm+1=nm+1+(m+1)Δ,于是 Δ=0,于是对于 m,也有 Sm(n)=S^m(n),符合数学归纳法,等式成立。

分拆数(Partition

施罗德数(Schro¨der

posted @   JPGOJCZX  阅读(18)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示