[HNOI2017]抛硬币(组合数学,扩展lucas)

Description

小A和小B抛硬币,小A抛a次,小B抛b次,求小A赢过小B的方案数。

Solution

这道题 ab a − b 较小,所以可以考虑关于 ab a − b 的式子。

  • a=b a = b ,若 a=b a = b ,则唯一需要考虑的平局的情况(因为情况是对称的)。
    s s 表示平局的方案数,则

    s=i=0aCaiCai=i=0aCaaiCai=C2aa

    ans=2a+bs2 a n s = 2 a + b − s 2

  • a>b a > b ,则无论如何也有一种情况使得小A赢,则只需要加上两种对称情况小A都赢的次数 s s
    设小B赢了i次,小A赢了 i+j i + j 次。

    a(i+j)>bijab1 a − ( i + j ) > b − i ⇒ j ⩽ a − b − 1

    s=i=0bj=1j=ab1CibCai+j=i=0bj=1j=ab1CibCi+ja=i=0bj=1j=ab1CbibCi+ja=j=1ab1Cb+ja+b s = ∑ i = 0 b ∑ j = 1 j = a − b − 1 C b i C a i + j = ∑ i = 0 b ∑ j = 1 j = a − b − 1 C b i C a i + j = ∑ i = 0 b ∑ j = 1 j = a − b − 1 C b b − i C a i + j = ∑ j = 1 a − b − 1 C a + b b + j

    然后用扩展lucas搞一搞就行了。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 10005, mod = (int)1e9;
typedef long long lint;

int pow(int x, lint k, int mod)
{
    int ret = 1;
    while (k) {
        if (k & 1) ret = (lint)ret * x % mod;
        x = (lint)x * x % mod; k >>= 1;
    }
    return ret;
}

const int p1 = 512, p2 = 1953125, inv1 = 109, inv2 = 1537323, inv3 = 976563;
int fac1[p1 + 5], fac2[p2 + 5];

void exgcd(lint a, lint b, lint &x, lint &y)
{
    if (b == 0) x = 1, y = 0;
    else exgcd(b, a % b, y, x), y -= x * (a / b);
}

int inv(int x, int p)
{
    lint a, b;
    exgcd(x, p, a, b);
    a = (a % p + p) % p;
    return a;
}

void prepare()
{
    fac1[0] = 1;
    for (int i = 1; i <= p1; ++i) fac1[i] = (lint)(i % 2 ? i : 1) * fac1[i - 1] % p1;
    fac2[0] = 1;
    for (int i = 1; i <= p2; ++i) fac2[i] = (lint)(i % 5 ? i : 1) * fac2[i - 1] % p2;
}

int fact1(lint n)
{
    if (n <= 0) return 1;
    return (lint)pow(fac1[p1], n / p1, p1) * fac1[n % p1] % p1 * fact1(n / 2) % p1;
}

int C1(lint n, lint m, bool type)
{
    int A = fact1(n), B = fact1(n - m), C = fact1(m), cnt = 0;
    for (lint x = n; x; x >>= 1) cnt += x / 2;
    for (lint x = n - m; x; x >>= 1) cnt -= x / 2;
    for (lint x = m; x; x >>= 1) cnt -= x / 2;
    if (type) return (lint)A * inv(B, p1) % p1 * inv(C, p1) % p1 * pow(2, cnt, p1) % p1;
    else return (lint)A * inv(B, p1) % p1 * inv(C, p1) % p1 * pow(2, cnt - 1, p1) % p1;
}

int fact2(lint n)
{
    if (n <= 0) return 1;
    return (lint)pow(fac2[p2], n / p2, p2) * fac2[n % p2] % p2 * fact2(n / 5) % p2;
}

int C2(lint n, lint m, bool type)
{
    int A = fact2(n), B = fact2(n - m), C = fact2(m), cnt = 0;
    for (lint x = n; x; x /= 5) cnt += x / 5;
    for (lint x = n - m; x; x /= 5) cnt -= x / 5;
    for (lint x = m; x; x /= 5) cnt -= x / 5;
    if (type) return (lint)A * inv(B, p2) % p2 * inv(C, p2) % p2 * pow(5, cnt, p2) % p2;
    else return (lint)A * inv(B, p2) % p2 * inv(C, p2) % p2 * pow(5, cnt, p2) % p2 * inv3 % p2;
}

int C(lint n, lint m, bool type)
{
    int A = C1(n, m, type), B = C2(n, m, type);
    A = (lint)(A * p2 % mod) * inv1 % mod;
    B = (lint)(B * p1 % mod) * inv2 % mod;
    return (A + B) % mod;
}

void print(int x, int k)
{
    int a[15] = {0}, cnt = 0;
    while (x) a[++cnt] = x % 10, x /= 10;
    for (int i = k; i >= 1; --i) putchar(a[i] + '0');
    puts("");
}

int main()
{
    freopen("coin.in", "r", stdin);
    freopen("coin.out", "w", stdout);

    prepare();

    lint a, b, k;
    while (scanf("%lld%lld%lld", &a, &b, &k) == 3) {
        if (a == b) print(((pow(2, a + b - 1, mod) - C(a + b, a, 0)) % mod + mod) % mod, k);
        else {
            lint ans = pow(2, a + b - 1, mod);
            if (((a + b) & 1) == 0) {
                ans += C(a + b, (a + b) / 2, 0);
                if (ans >= mod) ans -= mod;
            }
            for (lint i = (a + b) / 2 + 1; i <= a - 1; ++i) {
                ans += C(a + b, i, 1);
                if (ans >= mod) ans -= mod;
            }
            print(ans, k);
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值