斯特林数:
在组合数学,Stirling数可指两类数,第一类Stirling数和第二类Stirling数,都是由18世纪数学家James Stirling提出的。后来哥本哈根(Copenhagen)大学的尼尔森(Niels Nielsen,1865-1931)提出了"Stirlingschen Zahlen erster Art" [第一类Stirling数]和"Stirlingschen Zahlen zweiter Art" [第二类Stirling数],首次把这两类数冠以「Stirling数」之名 。因为苏格兰数学家斯特林(J. Stirling, 1692-1770)首次发现这些数并说明了它们的重要性。
-
第一类斯特林数
第一类Stirling数,也可记为
。 表示将 n 个不同元素构成m个圆排列(百度一下你就知道)的数目。同时还分为无符号第一类Stirling数
和带符号第一类Stirling数
。
推导:
无符号第一类Stirling数的递推式可以从其定义来推导。
S1(p,p)=1(p>=0),有p个人和P个圆圈,每个圆圈就只有一个人
S1(P,0)=0(P>=1)如果至少有1个人,那么任何安排都至少包含一个圆圈
考虑其定义如果要将n+1元素构成m个圆排列,考虑第n+1个元素:
(1)如果n个元素构成了m-1个圆排列,那么第n+1个元素独自构成一个圆排列。方案数:
(2)如果n个元素构成了m个圆排列,将第n+1个元素插入到任意元素的左边。方案数:
综合两种情况得:
综上:
代码:
long long s1[maxn][maxn];//存放第一类Stirling数
long long mod=1e9+7;//取模
void init()
{
s1[1][1]=1;
for(int i=2;i<maxn;i++)
for(int j=1;j<maxn;j++)
s1[i][j]=(s1[i-1][j-1]+(i-1)*s1[i-1][j])%mod;
}
例题:HDU 3472 Count the Buildings
题意: 给定一系列楼房,都在一条水平线上,高度从1到n,从左侧看能看到f个, 从右侧看,能看到b个,问有多少种这样的序列。
思路: 因为肯定能看到最高的,,那我们先假定最高的楼房位置确定,那么在其左边还有f-1个能看见,在其右边还有b-1个,能看见。。所以可以这样将题目转化: 将除最高楼之外的n-1个楼,分成f-1+b-1 组,在最高楼左边f-1 组,在其右边b-1组,那么分成f-1+b-1 组 就是第一类Stirling数。s[n-1][f-1+b-1]。。左边f-1 组,在其右边b-1组,就是将这f-1+b-1 组,组合c(f-1+b-1,f-1)。可以先把n-1个元素分成x-1+y-1组,然后每组内部做环排列。再在所有组中选取x-1组放到楼n的左边。所以答案ans= C[f + b - 2][f - 1] * S[n - 1][f + b - 2];
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const long long mod=1000000007;
long long s[2002][2002],c[2002][2002];
int main()
{
memset(s,0,sizeof(s));
memset(c,0,sizeof(c));
for(int i=1;i<=2000;i++)
{
c[i][0]=1;
s[i][0]=0;
}
s[1][1]=1;c[1][1]=1;
for(int i=2;i<=2000;i++)
for(int j=1;j<=i;j++)
{
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
s[i][j]=(s[i-1][j-1]+(i-1)*s[i-1][j])%mod;
}
int sec,x,y,n;
scanf("%d",&sec);
for(int z=1;z<=sec;z++)
{
scanf("%d%d%d",&n,&x,&y);
long long ans;
if(x+y-2<=2000)ans=(s[n-1][x+y-2]*c[x+y-2][x-1])%mod;else ans=0%mod;//防止RE
printf("%I64d\n",ans);
}
return 0;
}
有符号的第一类Stirling数可以从其表示的降阶函数归纳证明(简单写如下): (这点我没看,我也不懂,先放在这里)
依次把在左右两边的系数提取出来得:
第一类斯特林数除了可以表示升阶函数和降阶函数的系数之外还可以应用到一些实际问题上,比如很经典的解锁仓库问题。
问题:
有 n 个仓库,每个仓库有两把钥匙,共 2n 把钥匙。同时又有 n 位官员。
求:①如何放置钥匙使得所有官员都能够打开所有仓库?(只考虑钥匙怎么放到仓库中,而不考虑官员拿哪把钥匙。)
②如果官员分成 m 个不同的部,部中的官员数量和管理的仓库数量一致。那么有多少方案使得,同部的所有官员可以打开所有本部管理的仓库,而无法打开其他部管理的仓库?(同样只考虑钥匙的放置。)
思路:
① 打开仓库将钥匙放入仓库构成一个环:1号仓库放2号钥匙,2号仓库放3号钥匙……n号仓库放1号钥匙,这种情况相当于钥匙和仓库编号构成一个圆排列方案数是 (n-1)! 种。
② 对应的将 n 个元素分成 m 个圆排列,方案数就是第一类斯特林数 S1(n,m),若要考虑官员的情况,只需再乘上 n! 即可。
-
第二类斯特林数
第二类Stirling数,同时可记为
[与第一类的表示有大小写的区别] 。 表示将n个不同的元素分成m个集合的方案数。即把n个不同的小球放在m个相同的盒子里方案数,盒子不为空。
推导:
S2(P,P)=1(P>=0)
S2(P,0)=0 (p>=1)
把{1,2,3,4......p}分到k个非空且不可区分的盒子里的划分有两种情况:
1.P单独在一个集合,存在S2(p-1,k-1)种划分个数
2.p不单独在一个盒子的划分,p和其他元素在一个集合,也就是在没有放P之前,有p-1个元素已经分到了K个非空且不可区分的盒子里,现在把P放进去,有K种选择,存在K*S2(p-1,k)种划分
递推公式:S2(p,k)=k*S2(p-1,k)+S2(p-1,k-1) (1<=k<=p-1)
扩展:K!* S(P,K)计数就是把P元素集合划分到K个可区分的盒子里,且没有空盒子的划分个数
代码:
const int mod=1e9+7;//取模
LL s2[N][N];//存放要求的Stirling数
void init(){
memset(s2,0,sizeof(s2));
s2[1][1]=1;
for(int i=2;i<=N-1;i++){
for(int j=1;j<=i;j++){
s2[i][j]= (s2[i-1][j-1]+j*s2[i-1][j] )%mod ;
}
}
}
应用:
转自 https://blog.csdn.net/u011815404/article/details/80083954
两道题区分第一类斯特林数和第二类斯特林数:
1.(第一类)暑假里,小L到海边去玩,捡了nn个不同的贝壳。现在她想把这些贝壳串成kk个项链(项链是环形的)。她忽然很疑惑,这有多少种方案呢?
2.(第二类)小L准备把这些项链送给她的朋友们。假设有nn条项链,项链各不相同。她准备了kk个一模一样的礼盒,她准备将项链装进礼盒里且不让任何礼盒是空的。现在她又想知道有多少种方案了。