【SDOI 2010】古代猪文(组合计数)

本文解析了一道算法题“古代猪文”,该题要求计算特定组合数的幂次模运算。文章提供了详细的解题思路,包括如何通过分解质因数和利用中国剩余定理来简化计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:【BZOJ 1951】古代猪文

题目大意:给定正整数 q,n(1q,n109) q , n ( 1 ≤ q , n ≤ 10 9 ) ,求 qd|nCdnmod999911659 q ∑ d | n C n d mod 999911659

q=999911659 q = 999911659 ,则上试结果为 0 0
否则,因为999911659是素数,所以 q,n q , n 互素。由欧拉定理,得:

qd|nCdnqd|nCdnmod999911658mod999911659 q ∑ d | n C n d ≡ q ∑ d | n C n d mod 999911658 mod 999911659

关键在于计算 d|nCdnmod999911658 ∑ d | n C n d mod 999911658 的值。
发现模数太大了,不能直接用Lucas定理求解。我们要换一种思路。

分解素因数得: 999911658=2×3×4679×35617 999911658 = 2 × 3 × 4679 × 35617 。我们只要分别计算 d|nCdnmod2,3,4679,35617 ∑ d | n C n d mod 2 , 3 , 4679 , 35617 的值,就可以用中国剩余定理或者exgcd计算出指数,从而用快速幂算出最终的答案。

#include <cstdio>
typedef long long ll;
const ll mod = 999911659;
const ll mo[] = {2, 3, 4679, 35617};
const ll m[] = {mod / 2, mod / 3, mod / 4679, mod / 35617};
const ll maxm = 35627;
ll n, g, cmo, cm, fac[maxm], inv[maxm], res, ans;
ll mpow(ll x, ll y, ll z) {
    ll u = 1;
    for (; y; y >>= 1) {
        if (y & 1) {
            u = x * u % z;
        }
        x = x * x % z;
    }
    return u;
}
ll calc(ll a, ll b) {
    if (b < 0 || b > a) {
        return 0;
    } else if (b == 0 || b == a) {
        return 1;
    } else {
        return fac[a] * inv[a - b] % cmo * inv[b] % cmo;
    }
}
ll lucas(ll a, ll b) {
    if (a | b) {
        return lucas(a / cmo, b / cmo) * calc(a % cmo, b % cmo) % cmo;
    } else {
        return 1;
    }
}
int main() {
    scanf("%lld %lld", &n, &g);
    if (g == mod) {
        puts("0");
        return 0;
    }
    for (int k = 0; k < 4; k++) {
        cmo = mo[k], cm = m[k];
        fac[0] = 1, inv[0] = 1;
        for (int i = 1; i < cmo; i++) {
            fac[i] = fac[i - 1] * i % cmo;
            inv[i] = mpow(fac[i], cmo - 2, cmo);
        }
        res = 0;
        for (int i = 1; i * i <= n; i++) {
            if (n % i == 0) {
                if (i * i == n) {
                    res = (res + lucas(n, i)) % cmo;
                } else {
                    res = (res + lucas(n, i)) % cmo;
                    res = (res + lucas(n, n / i)) % cmo;
                }
            }
        }
        ll t = mpow(cm, cmo - 2, cmo) * cm % (mod - 1);
        ans = (ans + t * res % (mod - 1)) % (mod - 1);
    }
    printf("%lld\n", mpow(g, ans, mod));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值