有趣的数(组合计数,CCF)
题意
我们把一个数称为有趣的,当且仅当:
- 它的数字只包含 \(0,1,2,3\),且这四个数字都出现过至少一次。
- 所有的 \(0\) 都出现在所有的 \(1\) 之前,而所有的 \(2\) 都出现在所有的 \(3\) 之前。
- 最高位数字不为 \(0\)。
因此,符合我们定义的最小的有趣的数是 \(2013\)。
除此以外,\(4\) 位的有趣的数还有两个:\(2031\) 和 \(2301\)。
请计算恰好有 \(n\) 位的有趣的数的个数。
由于答案可能非常大,只需要输出答案除以 \(10^9+7\) 的余数。
数据范围
\(4 \leq n \leq 1000\)
思路
感觉这道题的题目描述挺像高中数学排列组合题的,因此先考虑组合计数方法。
组合计数题通常的思考方式就是分类讨论。这道题可以将\(0,1\)看成一组,\(2,3\)看成一组,一组的数量确定了,另一组的数量也就跟着确定了。
因此,我们可以分类讨论\(0,1\)组的数量。因为每个数组都至少出现过\(1\)次,因此\(0,1\)组的数量可以是\(2 \sim n - 2\)中的任意数。
假设数量是\(k\),那么就从\(n - 1\)位(第一位不能选)选\(k\)位,因此就是\(C_{n - 1}^k\)种。
然后再讨论每个数具体个数,那么\(0\)可能有\(1 \sim k - 1\),因此就有\(k - 1\)种情况;对应的,\(2\)可能有\(1 \sim n - k - 1\),因此就有\(n - k - 1\)种可能。
因此,最终的答案为:
\[\sum_{k = 2}^{n - 2} C_{n - 1}^k * (k - 1) * (n - k - 1)
\]
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1010, mod = 1e9 + 7;
int n;
ll c[N][N];
int main()
{
scanf("%d", &n);
for(int i = 0; i <= n; 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;
}
}
ll ans = 0;
for(int i = 2; i <= n - 2; i ++) {
ans = (ans + c[n - 1][i] * (i - 1) * (n - i - 1)) % mod;
}
printf("%lld\n", ans);
return 0;
}