• [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{ egin{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} ight.]

    [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) )

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

  • 相关阅读:
    多条件搜索问题 -sql拼接与参数化查询
    MVC View中获取action、controller、area名称、参数
    Hadoop权限认证的执行流程
    Java API操作HA方式下的Hadoop
    利用HBase的快照功能来修改表名
    hive两大表关联优化试验
    Spark SQL与Hive on Spark的比较
    Spark的RDD原理以及2.0特性的介绍
    hbase Java API 介绍及使用示例
    初识Spark2.0之Spark SQL
  • 原文地址:https://www.cnblogs.com/alecli/p/9978101.html
Copyright © 2020-2023  润新知