[BZOJ1951][SDOI2010]古代猪文(Lucas定理+中国剩余定理)

本文介绍了一种求解特定形式组合数之和的方法,并给出了一种有效的算法实现。利用费马小定理和Lucas定理进行模运算简化,通过中国剩余定理解决大数问题。

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

看到题目和题面想笑……不多说了,进入正题。
题意:求
Gd|NCdNmod999911659
的值。
首先,可以根据费马小定理得出,原式和
G(d|NCdN)mod999911658
在模999911659意义下是同余的。
而只要求得了(d|NCdN)mod999911658的值就很容易了。
首先将999911658分解质因数,得999911658=23467935617
而对于(d|NCdN)mod2(d|NCdN)mod3(d|NCdN)mod4679(d|NCdN)mod35617,虽然N很大,但是可以发现模数较小,所以可以使用Lucas定理求解这4个结果。
求得这4个结果后,就可以用中国剩余定理合并了。
注意特判G0(mod999911659)的情况。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll LPF = 999911659, PYZ = LPF - 1, N = 1e5 + 5;
ll n, G, ex[N], num[] = {0, 2, 3, 4679, 35617}, f[N], Inv[N];
ll qpow(ll a, ll b, ll pyz) {
    if (a == 0) return 0;
    if (a % pyz == 0) return b == 0;
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % pyz;
        a = a * a % pyz;
        b >>= 1;
    }
    return res;
}
void exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) return (void) (x = 1, y = 0);
    exgcd(b, a % b, y, x); y -= a / b * x;
}
ll inv(ll a, ll pyz) {
    ll x, y; exgcd(a, pyz, x, y);
    x = (x % pyz + pyz) % pyz;
    return x;
}
ll crt(ll pyz) {
    int i; ll ans = 0; for (i = 1; i <= 4; i++)
        (ans += ex[i] * (pyz / num[i]) % pyz *
            inv(pyz / num[i], num[i]) % pyz) %= pyz;
    return ans;
}
ll Lucas(ll n, ll m, ll pyz) {
    if (n < m) return 0;
    if (n < pyz && m < pyz) return f[n] * Inv[m] % pyz * Inv[n - m] % pyz;
    return Lucas(n / pyz, m / pyz, pyz) * Lucas(n % pyz, m % pyz, pyz) % pyz;
}
ll solve(ll lpf) {
    int i; f[0] = 1;
    for (i = 1; i < lpf; i++) f[i] = f[i - 1] * i % lpf;
    Inv[lpf - 1] = lpf - 1; for (i = lpf - 2; i >= 0; i--)
        Inv[i] = Inv[i + 1] * (i + 1) % lpf;
    ll S = sqrt(n), ans = 0;
    for (i = 1; i <= S; i++) if (n % i == 0) {
        (ans += Lucas(n, i, lpf)) %= lpf;
        if (i != n / i) (ans += Lucas(n, n / i, lpf)) %= lpf;
    }
    return ans;
}
int main() {
    int i; cin >> n >> G; if (G % LPF == 0) return puts("0"), 0;
    for (i = 1; i <= 4; i++) ex[i] = solve(num[i]);
    cout << qpow(G, crt(PYZ), LPF) << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值