Bzoj1951: [Sdoi2010]古代猪文

题面

传送门

Sol

超级数学板子题!!!

费马小定理,扩展欧几里德定理,中国剩余定理,卢卡斯定理等

题意就是求

Gk|NCkN mod 999911659 G ∑ k | N C N k   m o d   999911659

首先根据扩展欧拉定理或者费马小定理
axax%(p1)(mod p) a x ≡ a x % ( p − 1 ) ( m o d   p )
p p 为质数

那么直接求那个
k|NCNk(mod 999911658)
就好了

模数不是质数 lucas+CRT l u c a s + C R T 合并即可

然后 CRT C R T 用上 ExGcd E x G c d

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;

template <class Int>
IL void Input(RG Int &x){
    RG int z = 1; RG char c = getchar(); x = 0;
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    x *= z;
}

const int mod(999911659);
const int phi(999911658);
const int maxn(40000);

int prime[5] = {0, 2, 3, 4679, 35617};
int n, g, fac[5][maxn], inv[5][maxn], ans;

IL int Pow(RG ll x, RG ll y, RG int p){
    RG ll ret = 1;
    for(; y; y >>= 1, x = x * x % p)
        if(y & 1) ret = ret * x % p;
    return ret;
}

IL int ExGcd(RG int a, RG int b, RG int &x, RG int &y){
    if(!b){
        x = 1, y = 0;
        return a;
    }
    RG ll d = ExGcd(b, a % b, y, x);
    y -= 1LL * a / b * x;
    return d;
}

IL int C(RG int x, RG int y, RG int tp){
    return 1LL * fac[tp][x] * inv[tp][y] % prime[tp] * inv[tp][x - y] % prime[tp];
}

IL int Lucas(RG int x, RG int y, RG int tp){
    if(y > x) return 0;
    if(!y) return 1;
    if(x < prime[tp] && y < prime[tp]) return C(x, y, tp);
    RG ll ret = Lucas(x % prime[tp], y % prime[tp], tp);
    return 1LL * ret * Lucas(x / prime[tp], y / prime[tp], tp) % prime[tp];
}

IL int CRT(RG int k){
    RG int x, y, a[5], ret = 0, tmp;
    for(RG int i = 1; i <= 4; ++i){
        a[i] = Lucas(n, k, i);
        ExGcd(tmp = phi / prime[i], prime[i], x, y);
        ret = (ret + 1LL * x * a[i] % phi * tmp % phi) % phi;
    }
    return (ret + phi) % phi;
}

IL void Init(){
    for(RG int j = 1; j <= 4; ++j) fac[j][0] = inv[j][0] = 1;
    for(RG int j = 1; j <= 4; ++j)
        for(RG int i = 1; i < prime[j]; ++i)
            fac[j][i] = 1LL * fac[j][i - 1] * i % prime[j];
    for(RG int j = 1; j <= 4; ++j)
        inv[j][prime[j] - 1] = Pow(fac[j][prime[j] - 1], prime[j] - 2, prime[j]);
    for(RG int j = 1; j <= 4; ++j)
        for(RG int i = prime[j] - 2; i; --i)
            inv[j][i] = 1LL * inv[j][i + 1] * (i + 1) % prime[j];
}

int main(RG int argc, RG char* argv[]){
    Input(n), Input(g);
    if(g % mod == 0) return puts("0"), 0;
    Init();
    for(RG int i = 1; i * i <= n; ++i)
        if(n % i == 0){
            ans = (ans + CRT(i)) % phi;
            RG int j = n / i;
            if(i != j) ans = (ans + CRT(j)) % phi;
        }
    printf("%d\n", Pow(g, ans, mod));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值