题目传送门
题目描述:
n个数分成若干个集合的方案数(雾)
Solution
这道题是一道斯塔林数,可以用递归实现,但是我们这里尝试dp。
- 设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示i个数,分成j个集合上午方案数。
- 我们分来讨论:
当第i个数单独开辟一个集合时,方案数为 f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i−1][j−1]
当第i个数和前面的集合合并时,方案数为 f [ i − 1 ] [ j ] ∗ j f[i-1][j]*j f[i−1][j]∗j - 这样一层一层递推便可得到每一个 f [ i ] [ j ] f[i][j] f[i][j]的值。
而由于i个数的组合有
C
n
i
C_n^i
Cni种。所以答案为:
∑
i
=
0
n
−
1
∑
j
=
0
i
f
[
i
]
[
j
]
∗
C
n
i
\sum_{i=0}^{n-1}\sum_{j=0}^{i}f[i][j]*C_n^i
i=0∑n−1j=0∑if[i][j]∗Cni
注:这里i的循环不能到n,因为n个数的方案是我们希望求的,这里并不能累加
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p = 1e9+7;
int n;
int c[4020][4020];
int f[4020][4020];
signed main(){
scanf("%lld",&n);
for (int i=0;i<=n+10;i++) c[i][0] = c[i][i] = 1;
for (int i=2;i<=n;i++)
for (int j=1;j<i;j++) c[i][j] = (c[i-1][j]+c[i-1][j-1])%p;
f[0][0] = 1;
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++)
f[i][j] = ((f[i-1][j]*1ll*j)%p + f[i-1][j-1]%p)%p;
int ans = 0;
for (int i=0;i<n;i++)
for (int j=0;j<=i;j++)
ans+=(f[i][j]*1ll*c[n][i])%p,ans%=p;
printf("%lld",ans);
return 0;
}