luogu-P3726题解

简要题意

两个人抛硬币,求第一个人正面朝上次数大于第二个人的情况数对 \(p\) 取模,\(p\) 不是质数。

数据范围\(1\le a,b\le 10^{15},b\le a\le b+10^4\)

题解

首先你需要会扩展卢卡斯定理然后就只剩暴力推式子(逃

我们可以枚举两个人正面朝上次数,就可以列出一个式子:

\[\sum_{i=0}^a\sum_{j=0}^{i-1}{a\choose i}{b\choose j} \]

转换枚举方式,有:

\[\begin{aligned} & \sum_{i=1}^a\sum_{j=0}^{i+j\le s}{a\choose i+j}{b\choose j}\\ =& \sum_{i=1}^a\sum_{j=0}^{b}{a\choose i+j}{b\choose b-j}\\ =& \sum_{i=1}^a{a+b\choose b+i}\\ =& \sum_{i=b+1}^a{a+b\choose i}\\ \end{aligned} \]

考虑到 \(a,b\) 之间相差不大,考虑直接枚举每个 \(a+b\choose i\),但是似乎还是会超时。考虑到答案是杨辉三角中一排组合数的后缀,于是我们可以把答案拆成:

\[\sum_{i=b+1}^{{(a+b)\over2} - 1}{a+b\choose i}+\sum_{i={(a+b)\over2}}^{a+b}{a+b\choose i} \]

前面可以暴力地用扩展卢卡斯定理求,后面的就是 \(2^{a+b-1}\)。时间复杂度 \(O((a-b)\log^2a+5^k+2^k)\)

代码

这里只给出部分代码。

int C(ll n, ll m, int p, int pp, bool o){
    ll k = 0; if(n < m)return 0;
    for(ll i = n; i; i /= p)k += i / p;
    for(ll i = m; i; i /= p)k -= i / p;
    for(ll i = n - m; i; i /= p)k -= i / p;
    if(p == 2 and o)--k;
    if(k >= K)return 0;
    int t = 1ll * Fac(n, p, pp) * Inv(Fac(m, p, pp), pp) % pp * Inv(Fac(n - m, p, pp), pp) % pp * qmi(p, k, pp) % pp;
    if(p == 5 and o)t = 1ll * t * Inv(2, pp) % pp;
    return t;
}
int exLucas(ll n, ll m, bool o){
    for(int i = 1; i <= cnt; ++i)r[i] = C(n, m, p[i], pp[i], o);
    return crt();
}

signed main(){
    cnt = 2; fac[0][0] = fac[1][0] = 1;
    pp[0] = 512; pp[1] = 1953125; p[0] = 2, p[1] = 5;
    for(int q = 0; q < 2; ++q)for(int i = 1; i <= pp[q]; ++i){
        fac[q][i] = fac[q][i - 1];
        if(i % p[q])fac[q][i] = 1ll * fac[q][i] * i % pp[q];
    }
    while(scanf("%lld %lld %lld", &n, &m, &K) ^ EOF){
        mod = qmi(10, K, 1e9 + 7);
        pp[1] = qmi(p[1] = 2, K, 1e9 + 5);
        pp[2] = qmi(p[2] = 5, K, 1e9 + 5);
        ll ans;
        if(n == m)ans = (qmi(2, n + m - 1, mod) - exLucas(n + m, n, 1) + mod) % mod;
        else{
            ans = qmi(2, n + m - 1, mod);
            for(ll i = (n + m) / 2 + 1; i < n; ++i)ans = (ans + exLucas(n + m, i, 0)) % mod;
            if((n + m) % 2 == 0)ans = (ans + exLucas(n + m, n + m >> 1, 1)) % mod;
        }
        while(ans < mod / 10)putchar('0'), mod /= 10;
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2025-03-16 22:09  Lyrella  阅读(16)  评论(0)    收藏  举报