斯特林数(Stirling数)
题目链接
3165. 第一类斯特林数
3166. 第二类斯特林数
3020. 建筑师
第一类斯特林数(无符号)性质
第一类Stirling数表示将 \(n\) 个不同元素构成 \(m\) 个圆排列的数目,记为 \(s(n,m)\) 或 \(\begin{bmatrix}n\\m \end{bmatrix}\)
递推式:\(s(n,m)=s(n-1,m-1)+(n-1)\times s(n-1,m)\)
①\(s(0,0)=1\)
②\(s(n,0)=0\)
③\(s(n,n)=1\)
④\(s(n,1)=(n-1)!\)
⑤\(s(n,n-1)=C_n^2\)
⑥\(s(n,2)=(n-1)!\sum_{i=1}^{n-1}\frac{1}{i}\)
⑦\(s(n,n-2)=2C_n^3+3C_n^4\)
⑧\(\sum_{k=0}^ns(n,k)=n!\)
第二类斯特林数性质
第二类Stirling数实际上是集合的一个拆分,表示将n个不同的元素拆分成m个集合的方案数,记为\(S(n,m)\) 或 \(\begin{Bmatrix}n\\m \end{Bmatrix}\)
递推式:\(S(n,m)=S(n-1,m-1)+m\times S(n-1,m)\)
通项公式:\(S(n,m)=\frac{1}{m!}\sum_{k=0}^{m}(-1)^kC_m^k(m-k)^n\)
①\(S(n,0)=0^n\)
②\(S(n,1)=1\)
③\(S(n,n)=1\)
④\(S(n,2)=2^{n-1}-1\)
⑤\(S(n,n-1)=C_n^2\)
⑥\(S(n,n-2)=C_n^3+3C_n^4\)
⑦\(S(n,3)=\frac{1}{2}(3^{n-1}+1)-2^{n-1}\)
⑧\(S(n,n-3)=C_n^4+10C_n^5+15C_n^6\)
两类斯特林数的关系(置换性):
例题
3165. 第一类斯特林数
第一类斯特林数(斯特林轮换数)\(\begin{bmatrix}n\\k \end{bmatrix}\) 表示将 \(n\) 个两两不同的元素,划分为 \(k\) 个非空圆排列的方案数。
现在,给定 \(n\) 和 \(k\),请你求方案数。
圆排列定义:圆排列是排列的一种,指从 \(n\) 个不同元素中取出 \(m(1≤m≤n)\) 个不同的元素排列成一个环形,既无头也无尾。两个圆排列相同当且仅当所取元素的个数相同并且元素取法一致,在环上的排列顺序一致。
输入格式
两个整数 \(n\) 和 \(k\)。
输出格式
输出一个整数表示划分方案数。
答案对 \(10^9+7\) 取模。
数据范围
\(1≤k≤n≤1000\)
输入样例:
3 2
输出样例:
3
- 时间复杂度:\(O(nk)\)
代码
#include<cstdio>
using namespace std;
const int mod=1e9+7;
int n,k;
int s[1005][1005];
int main()
{
scanf("%d%d",&n,&k);
s[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
s[i][j]=(s[i-1][j-1]+1ll*(i-1)*s[i-1][j])%mod;
printf("%d",s[n][k]);
return 0;
}
3166. 第二类斯特林数
第二类斯特林数(斯特林子集数)\(\begin{Bmatrix}n\\k \end{Bmatrix}\) 表示将 \(n\) 个两两不同的元素,划分为 \(k\) 个非空子集的方案数。
现在,给定 \(n\) 和 \(k\),请你求方案数。
输入格式
两个整数 \(n\) 和 \(k\)。
输出格式
输出一个整数表示划分方案数。
答案对 \(10^9+7\) 取模。
数据范围
\(1≤k≤n≤1000\)
输入样例:
3 2
输出样例:
3
- 时间复杂度:\(O(nk)\)
代码
#include<cstdio>
using namespace std;
const int mod=1e9+7;
int n,k;
int S[1005][1005];
int main()
{
scanf("%d%d",&n,&k);
S[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
S[i][j]=(S[i-1][j-1]+1ll*j*S[i-1][j])%mod;
printf("%d",S[n][k]);
return 0;
}
3020. 建筑师
小 \(Z\) 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 \(n\) 个建筑,每个建筑的高度是 \(1\) 到 \(n\) 之间的一个整数。
小 \(Z\) 有很严重的强迫症,他不喜欢有两个建筑的高度相同。
另外小 \(Z\) 觉得如果从最左边(所有建筑都在右边)看能看到 \(A\) 个建筑,从最右边(所有建筑都在左边)看能看到 \(B\) 个建筑,这样的建筑群有着独特的美感。
现在,小 \(Z\) 想知道满足上述所有条件的建筑方案有多少种?
如果建筑 \(i\) 的左(右)边没有任何建造比它高,则建筑 \(i\) 可以从左(右)边看到。
两种方案不同,当且仅当存在某个建筑在两种方案下的高度不同。
输入格式
第一行一个整数 \(T\),代表 \(T\) 组数据。
接下来 \(T\) 行,每行三个整数 \(n,A,B\)。
输出格式
对于每组数据输出一行答案 \(mod10^9+7\)。
数据范围
\(1≤n≤50000\),
\(1≤A,B≤100\),
\(1≤T≤200000\)
输入样例:
2
3 2 2
3 1 2
输出样例:
2
1
解题思路
第一类斯特林数
以最高的建筑作为分界线,左右分别分为 \(A-1\) 和 \(B-1\) 部分,设每一部分有 \(k\) 个建筑,除去该部分最高建筑外,有 \((k-1)!\) 种方案数,正好对应第一类斯特林数,即 \(s(n-1,A-1+B-1)\),现在就差分配方案数,可从 \(A-1+B-1\) 个建筑中选出 \(A-1\) 个建筑放在最高建筑的左边,即 \(C_{A+B-2}^{A-1}\),故总的方案数为 \(s(n-1,A+B-2) \times C_{A+B-2}^{A-1}\)
- 时间复杂度:\(O(T+n\times (A+B))\)
代码
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int c[205][205],s[50005][205];
int T,n,A,B;
void init()
{
for(int i=0;i<=200;i++)
for(int j=0;j<=i;j++)
if(!j)c[i][j]=1;
else
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
s[0][0]=1;
for(int i=1;i<=50000;i++)
for(int j=1;j<=200;j++)
s[i][j]=(s[i-1][j-1]+1ll*(i-1)*s[i-1][j])%mod;
}
int main()
{
init();
for(scanf("%d",&T);T;T--)
{
scanf("%d%d%d",&n,&A,&B);
printf("%d\n",1ll*c[A+B-2][A-1]*s[n-1][A+B-2]%mod);
}
return 0;
}