【第一类斯特林数】
1.定理
第一类斯特林数 S1(n,m) 表示的是将 n 个不同元素构成 m 个圆排列的数目。
2.递推式
设人被标上1,2,.....p,则将这 p 个人排成 m 个圆有两种情况:
- 在一个圆圈里只有标号为 p 的人自己,排法有 S1(n-1,m-1) 个。
- p 至少和另一个人在一个圆圈里。
这些排法通过把 1,2....n-1 排成 m 个圆再把 n 放在 1,2....n-1 任何一人左边得到,因此第二种类型排法共有 (n-1)*S1(n-1,m) 种。
我们所做的就是把 {1,2,...,p} 划分到 k 个非空且不可区分的盒子,然后将每个盒子中的元素排成一个循环排列。
综上,可得出第一类Stirling数定理:
边界条件:
- :有 n 个人和 n 个圆,每个圆只有一个人
- :如果至少有 1 个人,那么任何的安排都至少包含一个圆
3.应用举例
第一类斯特林数除了可以表示升阶函数和降阶函数的系数之外还可以应用到一些实际问题上,比如很经典的解锁仓库问题。
问题说明:
有 n 个仓库,每个仓库有两把钥匙,共 2n 把钥匙。同时又有 n 位官员。
求:①如何放置钥匙使得所有官员都能够打开所有仓库?(只考虑钥匙怎么放到仓库中,而不考虑官员拿哪把钥匙。)
②如果官员分成 m 个不同的部,部中的官员数量和管理的仓库数量一致。那么有多少方案使得,同部的所有官员可以打开所有本部管理的仓库,而无法打开其他部管理的仓库?(同样只考虑钥匙的放置。)
分析:
① 打开仓库将钥匙放入仓库构成一个环:1号仓库放2号钥匙,2号仓库放3号钥匙……n号仓库放1号钥匙,这种情况相当于钥匙和仓库编号构成一个圆排列方案数是 (n-1)! 种。
② 对应的将 n 个元素分成 m 个圆排列,方案数就是第一类斯特林数 S1(n,m),若要考虑官员的情况,只需再乘上 n! 即可。
4.算法实现
-
const
int mod=
1e9+
7;
//取模
-
LL s[N][N];
//存放要求的第一类Stirling数
-
void init(){
-
memset(s,
0,
sizeof(s));
-
s[
1][
1]=
1;
-
for(
int i=
2;i<=N
-1;i++){
-
for(
int j=
1;j<=i;j++){
-
s[i][j]=s[i
-1][j
-1]+(i
-1)*s[i
-1][j];
-
if(s[i][j]>=mod)
-
s[i][j]%=mod;
-
}
-
}
-
}
【第二类斯特林数】
1.定理
第二类斯特林数 S2(n,m) 表示的是把 n 个不同元素划分到 m 个集合的方案数。
2.递推式
元素在哪些集合并不重要,唯一重要的是各集合里装的是什么,而不管哪个集合装了什么。
考虑将前 n 个正整数,{1,2,...,n} 的集合作为要被划分的集合,把 {1,2,...,n} 分到 m 个非空且不可区分的集合的划分有两种情况:
- 那些使得 n 自己单独在一个集合的划分,存在有 S2(n-1,m-1) 种划分个数
- 那些使得 n 不单独自己在一个盒子的划分,存在有 m*S2(n-1,m) 种划分个数
考虑第二种情况,n 不单独自己在一个盒子,也就是 n 和其他元素在一个集合里面,也就是说在没有放 n 之前,有 n-1 个元素已经分到了m 个非空且不可区分的盒子里面(划分个数为 S2(n-1,m)),那么现在问题是把 n 放在哪个盒子里面,此时有 m 种选择,所以存在有 m*S2(n-1,m)
综上,可得出第二类斯特林数定理:
边界条件:
3.应用举例
第二类斯特林数主要是用于解决组合数学中的放球模型,主要是针对于球之前有区别的放球模型:
1)n 个不同的球,放入 m 个无区别的盒子,不允许盒子为空。
方案数:S2(n,m),与第二类斯特林数的定义一致。
2)n 个不同的球,放入 m 个有区别的盒子,不允许盒子为空。
方案数:m!*S2(n,m),因盒子有区别,乘上盒子的排列即可。
3)n 个不同的球,放入 m 个无区别的盒子,允许盒子为空。
方案数:,枚举非空盒的数目即可。
4)n 个不同的球,放入 m 个有区别的盒子,允许盒子为空。
① 方案数: ,枚举非空盒的数目,注意到盒子有区别,乘上一个排列系数即可。
② 既然允许盒子为空,且盒子间有区别,那么对于每个球有 m 种选择,每个球相互独立。有方案数:
4.算法实现
-
const
int mod=
1e9+
7;
//取模
-
LL s[N][N];
//存放要求的Stirling数
-
void init(){
-
memset(s,
0,
sizeof(s));
-
s[
1][
1]=
1;
-
for(
int i=
2;i<=N
-1;i++){
-
for(
int j=
1;j<=i;j++){
-
s[i][j]=s[i
-1][j
-1]+j*s[i
-1][j];
-
if(s[i][j]>=mod)
-
s[i][j]%=mod;
-
}
-
}
-
}
5.扩展:S(n,m) 的奇偶性
求 S(n,m) 的奇偶性实质就是求 S(n,m)%2
对于第二类斯特林数,首先有:S[i][j]=S[i−1][j−1]+j∗S[i−1][j]
那么在模 2 的情况下 ,有:
- 当 j 为偶数:S[i][j] ≡(S[i−1][j−1]%2)
- 当 j 为奇数:S[i][j] ≡((S[i−1][j−1]+S[i−1][j])%2)
于是,可以倒过来,即有:
- 当 j 为偶数时:S[i][j] 会被加到 S[i+1][j+1] 和 S[i+1][j]
- 当 j 为奇数时:S[i][j] 会被加到 S[i+1][j+1]
于是,令 a=n−m,b=(m+1)/2,则答案就相当于把 a 个相同的球分成 b 组,每组个数可以为 0 的方案总数,即:C(b−1,a+b−1)
然后利用 C(n,m) 为奇数时有 n&m=n 的结论,进行判断即可
-
int calc(int n,int m){
-
return (m&n)==n;
-
}
-
int main(){
-
int n,m;
-
scanf(
"%d%d",&n,&m);
-
if(n==
0&&m==
0)
//特判
-
printf(
"1\n");
-
else
if(n==
0||m==
0||n<m)
//特判
-
printf(
"0\n");
-
else{
-
int a=n-m;
-
int b=(m+
1)/
2;
-
int res=calc(b
-1,a+b
-1);
-
printf(
"%d\n",res);
-
}
-
return
0;
-
}
【两类斯特林数的关系】
两类斯特林数之间的递推式和实际含义很类似,他们之间存在一个互为转置的转化关系:
【例题】
- Binary Stirling Numbers(POJ-1430)(第二类斯特林数):点击这里