P3904 三只小猪 题解

· · 题解

第二类斯特林数。

Description

第二类斯特林数

定义

n 个不同元素,划分为 m非空子集的方案数,记作 \begin{Bmatrix}n\\m\end{Bmatrix}

递推公式

\begin{Bmatrix}n\\m\end{Bmatrix} = \begin{Bmatrix}n-1\\m-1\end{Bmatrix}+m\begin{Bmatrix}n-1\\m\end{Bmatrix}

放到这个题下证明一下。

考虑第 $n$ 个小猪进房间的情况: 1. 若单独进一个房间,前面的 $n-1$ 个小猪就要进 $m-1$ 个房间里,方案数就是 $\begin{Bmatrix}n-1\\m-1\end{Bmatrix}$; 2. 若进已经有小猪的房间里,就先让前 $n-1$ 个小猪进 $m$ 个房间里,第 $n$ 个小猪可以进 $m$ 个房间中任意一个房间,不重不漏,这样的方案数就是 $m\begin{Bmatrix}n-1\\m\end{Bmatrix}$。 分类加法,最后的方案数就是 $$ \begin{Bmatrix}n\\m\end{Bmatrix} = \begin{Bmatrix}n-1\\m-1\end{Bmatrix}+m\begin{Bmatrix}n-1\\m\end{Bmatrix} $$ # Solution 知道了第二类斯特林数的递推公式,我们就可以对这个题进行求解了。 但是斯特林数的增长比组合数还要快,$n\leq 50$ 就会爆 long long,因此需要高精。 $S_{i,j,k}$ 表示第二类斯特林数 $\begin{Bmatrix}i\\j\end{Bmatrix}$ 的第 $k$ 位是多少,$L_{i,j}$ 表示 $\begin{Bmatrix}i\\j\end{Bmatrix}$ 有多少位,然后高精乘法即可。 # Code ```cpp #include<bits/stdc++.h> //#define int long long #define ll long long #define next nxt #define re register #define il inline const int N = 50 + 5; using namespace std; int max(int x,int y){return x > y ? x : y;} int min(int x,int y){return x < y ? x : y;} int n,m; int S[N][N][105],L[N][N]; il int read() { int f=0,s=0; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) f |= (ch=='-'); for(; isdigit(ch);ch=getchar()) s = (s<<1) + (s<<3) + (ch^48); return f ? -s : s; } il void calc(int x,int y) { L[x][y] = max(L[x-1][y-1],L[x-1][y]); for(re int i=1;i<=L[x][y];i++) { S[x][y][i] += S[x-1][y-1][i] + y * S[x-1][y][i]; S[x][y][i+1] += S[x][y][i] / 10; S[x][y][i] %= 10; } if(S[x][y][L[x][y]+1] > 0) L[x][y]++;//看看能不能更新最高位 while(S[x][y][L[x][y]] >= 10) { S[x][y][L[x][y]+1] = S[x][y][L[x][y]] / 10; S[x][y][L[x][y]] %= 10; L[x][y]++; } } signed main() { S[0][0][1] = 1 , L[0][0] = 1; n = read() , m = read(); if(!m || m > n) return printf("0"),0;//特判等于0的情况 for(re int i=1;i<=n;i++) for(re int j=1;j<=min(i,m);j++) calc(i,j); for(re int i=L[n][m];i>=1;i--) cout << S[n][m][i]; return 0; } ```