最近看的数数题比较多。。于是滚来写组合数学的博客
第一类斯特林数
定义
把n个不同数划分为m个圆排列的方案数
递推式
-
如果第 n n n个数自成一个圆,那么 S 1 n , m = S 1 n − 1 , m − 1 S1_{n,m}=S1_{n-1,m-1} S1n,m=S1n−1,m−1
-
否则第 n n n个数在 n − 1 n-1 n−1个数构成的 m m m个圆排列中,随便加入一个圆
而且要注意圆排列是有顺序的,于是加入的数量应该不是圆的数量 m m m,而是数的个数 n − 1 n-1 n−1
此时 S 1 n , m = ( n − 1 ) S 1 n − 1 , m S1_{n,m}=(n-1)S1_{n-1,m} S1n,m=(n−1)S1n−1,m
总方案数 S 1 n , m = S 1 n − 1 , m − 1 + ( n − 1 ) S 1 n − 1 , m S1_{n,m}=S1_{n-1,m-1}+(n-1)S1_{n-1,m} S1n,m=S1n−1,m−1+(n−1)S1n−1,m
算法实现
for(int i=1;i<=50000;i++){
for(int j=1;j<=200;j++){
s[i][j]=(s[i-1][j-1]+(i-1)*(s[i-1][j])%mod)%mod;
}
}
应用
引用自:link
第一类斯特林数除了可以表示升阶函数和降阶函数的系数之外还可以应用到一些实际问题上,比如很经典的解锁仓库问题。
问题说明
有 n 个仓库,每个仓库有两把钥匙,共 2n 把钥匙。同时又有 n 位官员。》
求:
①如何放置钥匙使得所有官员都能够打开所有仓库?(只考虑钥匙怎么放到仓库中,而不考虑官员拿哪把钥匙。)
②如果官员分成 m 个不同的部,部中的官员数量和管理的仓库数量一致。那么有多少方案使得,同部的所有官员可以打开所有本部管理的仓库,而无法打开其他部管理的仓库?(同样只考虑钥匙的放置。)
分析
① 打开仓库将钥匙放入仓库构成一个环:1号仓库放2号钥匙,2号仓库放3号钥匙……n号仓库放1号钥匙,这种情况相当于钥匙和仓库编号构成一个圆排列方案数是 (n-1)! 种。
② 对应的将 n 个元素分成 m 个圆排列,方案数就是第一类斯特林数 S1(n,m),若要考虑官员的情况,只需再乘上 n! 即可。
第二类斯特林数
定义
把n个不同数划分到m个集合中的方案数
递推式
-
如果第 n n n个数自成集合,那么 S 2 n , m = S 2 n − 1 , m − 1 S2_{n,m}=S2_{n-1,m-1} S2n,m=S2n−1,m−1
-
否则第 n n n个数在 n − 1 n-1 n−1个数构成的 m m m个集合中,随便加入一个集合
这里跟第一类斯特林数不同,集合中没有顺序,于是加入的数量是集合m个
此时 S 2 n , m = m S 2 n − 1 , m S2_{n,m}=mS2_{n-1,m} S2n,m=mS2n−1,m
总方案数 S 2 n , m = S 2 n − 1 , m − 1 + m ∗ S 2 n − 1 , m S2_{n,m}=S2_{n-1,m-1}+m*S2_{n-1,m} S2n,m=S2n−1,m−1+m∗S2n−1,m
算法实现
for(int i=1;i<=50000;i++){
for(int j=1;j<=200;j++){
s[i][j]=(s[i-1][j-1]+j*(s[i-1][j])%mod)%mod;
}
}
应用
第二类斯特林数主要是用于解决组合数学中的放球模型,主要是针对于球之前有区别的放球模型:
分析
1)n 个不同的球,放入 m 个无区别的盒子,不允许盒子为空。
方案数: S 2 n , m S2_{n,m} S2n,m,与第二类斯特林数的定义一致。
2)n 个不同的球,放入 m 个有区别的盒子,不允许盒子为空。
方案数: m ! ∗ S 2 n , m m!*S2_{n,m} m!∗S2n,m,因盒子有区别,乘上盒子的排列即可。
3)n 个不同的球,放入 m 个无区别的盒子,允许盒子为空。
方案数: ∑ k = 0 m S 2 n , k \sum\limits_{k=0}^{m}S2_{n,k} k=0∑mS2n,k,枚举非空盒数量。
4)n 个不同的球,放入 m 个有区别的盒子,允许盒子为空。
很显然不是 m n m^n mn吗!
套斯特林数, ∑ k = 0 m P m , k ∗ S 2 n , k \sum\limits_{k=0}^{m}P_{m,k}*S2_{n,k} k=0∑mPm,k∗S2n,k
例题
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long read(){
long long x=0;char ch=getchar();long long pos=1;
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return pos?x:-x;
}
long long t,n,a,b,s[50001][301],jc[50001],c[301][301];
long long mod=1000000007;
int main(){
t=read();
for(long long i=0;i<=200;i++){
c[i][0]=1;
}
for(long long i=1;i<=200;i++){
for(long long j=1;j<=i;j++){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
s[0][0]=1;
for(long long i=1;i<=50000;i++){
for(long long j=1;j<=200;j++){
s[i][j]=(s[i-1][j-1]+(i-1)*(s[i-1][j])%mod)%mod;
}
}
for(long long i=1;i<=t;i++){
n=read(),a=read(),b=read();
printf("%lld\n",(c[a+b-2][a-1]*s[n-1][a+b-2])%mod);
}
return 0;
}