有趣的数(组合计数,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;
}
posted @ 2021-03-10 14:03  pbc的成长之路  阅读(111)  评论(0编辑  收藏  举报