ccf有趣的数java_CCF CSP 有趣的数

组合数学,概率论

问题描述

我们把一个数称为有趣的,当且仅当:

1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。

2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。

3. 最高位数字不为0。

因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。

请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。

输入格式

输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。

输出格式

输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。

样例输入

4

样例输出

3

===================================================================================================================

这道题分析后就可以用高中数学解了,研究生期间也会学组合数学

假设“有趣的数”分别包含 a, b, c, d 个0, 1, 2, 3.

先考虑 (0, 1) 的排列情况,由于0在1前,所以形式是0 0 .. 1 ...1 1。0不能在最高为,故最高位需要使用一个2(即最高位必须是2),剩下 (c + d - 1) 个 (2, 3).

由于 2 必须在3前面。所以把(c + d - 1)个 2 | 3插入 0 1串中,就是高中学的“插空法”。(a + b + 1)个数产生(a + b + 1 + 1)个空。最高位已确定为2,其他2(若有),插在最高位左右一样,所以就是把 (c + d - 1)个数插入(a + b + 1)个空中。

C(a + b + 1 + c + d - 1 - 1, c + d - 1) * (c + d - 1) * ( a + b - 1).

(c + d -1)为 (c + d)个(2, 3)中2或3的个数,(a + b - 1)为 (0, 1)中0或1的个数。

计算这个式子,得计算组合数,我用的快速幂+费马小定理求逆元。也可用 拓展欧几里得。

#include typedef long long LL;

const int MOD = 1e9 +7, N = 2017;

LL fac[N];

void init() {

fac[0] = fac[1] = 1;

for (int i = 2; i < N; ++i) {

fac[i] = fac[i - 1] * i % MOD;

}

}

LL pow_mod(LL a, LL n) {

LL base = a, res = 1;

while(n) {

if(n & 1) res = res * base % MOD;

base = base * base % MOD;

n >>= 1;

}

return res;

}

LL C(LL n, LL m) {

return fac[n] * pow_mod(fac[n - m], MOD - 2) % MOD * pow_mod(fac[m], MOD - 2) % MOD;

}

int main()

{

//ios::sync_with_stdio(false);

init();

int n;

scanf("%d", &n);

//2 3 最多共有(n-2)个

long long sum = 0;

for (int i23 = 1; i23 <= n - 2; ++i23) {

sum = (sum + C(n - 1, i23 - 1) * (i23 - 1) % MOD * (n - i23 -1) % MOD) % MOD;

}

printf("%lld\n", sum);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值