斯特林数(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\)

两类斯特林数的关系(置换性):

\[\sum_{k=0}^n\begin{bmatrix}n\\k \end{bmatrix}\begin{Bmatrix}k\\m \end{Bmatrix}=\sum_{k=0}^n\begin{Bmatrix}n\\k \end{Bmatrix}\begin{bmatrix}k\\m \end{bmatrix} \]

例题

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;
}
posted @ 2021-10-25 21:21  zyy2001  阅读(537)  评论(0编辑  收藏  举报