[SDOI2010]古代猪文

题意

Here

思考

简要题意:给定 \(G, n\),求:

\[G^{\sum_{k|n}C_n^k} (mod\ 999911659) \]

由于模数为质数,根据费马小定理:

\[G^{\sum_{k|n}C_n^k} \equiv G^{\sum_{k|n}C_n^k mod\ 999911658} (mod\ 999911659) \]

由于要求出模意义下的组合数,外加模数并非质数,我们可以考虑用扩展 \(Lucas\) 定理:

\(999911658 = 2 * 3 * 4679 * 35617\)

用中国剩余定理合并一下这四个同余方程组就行了:

\[\left\{ \begin{aligned} x & \equiv a_1 (mod \ 2)\\ x & \equiv a_2 (mod \ 3)\\ x & \equiv a_3 (mod \ 4679)\\ x & \equiv a_4 (mod \ 35617)\\ \end{aligned} \right.\]

\[a_i=\sum_{k|n}C_n^k(mod\ p_i) (p_i=2/3/4679/35617) \]

注意特判一下,费马小定理成立的条件是模为质数且与底数互质,当 \(G\ mod\ 999911659 = 0\) 时直接输出 \(0\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 100010;
ll qpow(ll x, ll p, ll mod){
    ll ans = 1, base = x;
    while(p){
        if(p & 1) ans = (ans * base) % mod;
        base = (base * base) % mod;
        p >>= 1;
    }
    return ans;
}
ll fact[N], A[N];
ll B[N] = {0, 2, 3, 4679, 35617};
ll init(ll mod){
    fact[0] = 1;
    for(ll i=1; i<=mod; i++) fact[i] = (fact[i-1] * i) % mod;
}
ll C(ll n, ll m, ll mod){
    if(n < m) return 0;
    return fact[n] * qpow(fact[m], mod-2, mod) % mod * qpow(fact[n-m], mod-2, mod) % mod;
}
ll lucas(ll n, ll m, ll mod){
    if(n < m) return 0;
    if(m == 0) return 1;
    return lucas(n/mod, m/mod, mod) * C(n%mod, m%mod, mod) % mod;
}
ll x, y;
ll exgcd(ll a, ll b, ll &x, ll &y){
    if(b == 0){
        x = 1; y = 0; return a;
    }
    ll ans = exgcd(b, a%b, x, y);
    ll tmp = x; x = y; y = tmp - a / b * y;
    return ans;
}
ll CRT(){
    ll M = 999911658, ans = 0;
    for(ll i=1; i<=4; i++){
        ll a = M / B[i], b = B[i];
        exgcd(a, b, x, y);
        x = (x + B[i]) % B[i];
        ans = (ans + x * a % M * A[i] % M) % M;
    }
    return (ans + M) % M;
}
ll n, G, tmp;
int main(){
    cin >> n >> G;
    if(G % 999911659 == 0){
        cout << 0;
        return 0;
    }
    for(ll k=1; k<=4; k++){
        init(B[k]);
        for(ll i=1; i*i<=n; i++){
            if(n % i == 0){
                A[k] = (A[k] + lucas(n, i, B[k])) % B[k];
                if(i * i != n){
                    A[k] = (A[k] + lucas(n, n / i, B[k])) % B[k];
                }
            }
        }
    }
    cout << qpow(G, CRT(), 999911659);
    return 0;
}

总结

总结一下这一题的思路:

  1. 分析题目得出式子
  2. 由于要求幂,指数巨大且模数为质数,想到费马小定理
  3. 指数为组合数,而快速求出组合数取模,用 \(Lucas\) 定理
  4. 直接 \(Lucas\) 搞不了,用扩展 \(Lucas\) ( \(+CRT\) )

总之如果会以上几个数论定理的话这题并不难想,主要是套了很多板子,板子要掌握好啊……

posted @ 2018-11-18 15:35  alecli  阅读(116)  评论(0编辑  收藏  举报