HDU --- 5446 Unknown Treasure 数论综合【Lucas定理 + 中国剩余定理 + 快速乘 + 思维】

来源:互联网 发布:外商投资网络支付机构 编辑:程序博客网 时间:2024/04/20 07:36

传送门
//题意: 给出n,m,k. 下一行给出k个质数. 求Cmnmodpi.
//一看n,m的范围都很大, mod的数也很大, 那么肯定不是简单的做法, 首先需要分析题意有
这里写图片描述
所以X就是我们要求的值, 我们可以发现等式右边我们可以用Lucas算出每一个具体的值, 然后得到了一系列类似于同余方程的方程组, 然后应用中国剩余定理求解出这些方程组就可以得到我们最后要的答案了.

//Lucas定理:
C(n, m) % p = C(n / p, m / p) * C(n%p, m%p) % p
对于C(n / p, m / p),如果n / p 还是很大,可以递归下去,一直到世界的尽头.
既是代码中的Lucas函数.(其中p为质数)

//这道题可是数论上的比较综合的题目了. 所以根据步骤来就是了, 我建议的是每写完一个子函数就调用一下验证其正确性, 反正博主是这样做的, 然后就1A 了. xx.

AC Code

/** @Cain*/const int maxn=15+5;ll n,m,k;ll modnum[maxn],yunum[maxn];//求a * b % mod; 由于这道题的mod都很大,所以乘法都容易爆,故需要快速乘.ll mut_mod(ll a,ll b,ll mod){    ll res = 0;    while(b){        if(b&1) res = (res + a) % mod;        a = (a + a) % mod;        b >>= 1;    }    return res;}//扩展GCD.ll ex_gcd(ll a,ll b,ll &x,ll &y){    if(!b){        x = 1; y = 0;        return a;    }    ll r = ex_gcd(b,a%b,x,y);    ll tmp = x;    x = y ; y = tmp - a/b*y;    return r;}//快速幂用于求逆元,虽然ex_gcd也行,但是我不会用. xx.ll qpow(ll x,ll y,ll mod){    ll res = 1;    while(y){        if(y & 1) res = mut_mod(res,x,mod) % mod;        x = mut_mod(x,x,mod) % mod;        y >>= 1;    }    return res;}//n % mod 的逆元ll inv(ll n,ll mod){    return qpow(n,mod-2,mod);}//求n! % modll fac(ll n,ll mod){    ll res = 1;    for(int i=2;i<=n;i++) res = res * i % mod;    return res;}//C(n, m) % modll Comb(ll n,ll m, ll mod){    if(m>n) return 0;    return fac(n,mod) * inv(fac(m,mod),mod) % mod * inv(fac(n-m,mod),mod) % mod;}//卢卡斯定理,求C(n,m)%mod,且n,m,mod都很大.ll Lucas(ll n, ll m,ll mod){    return m ? Lucas(n/mod, m/mod, mod) * Comb(n%mod, m%mod,mod) % mod : 1;}//解一阶同余方程ll tongyu(){    ll M = modnum[1],R = yunum[1];    for(int i=2;i<=k;i++){        ll d = __gcd(modnum[i],M);        ll c = yunum[i] - R;        if(c%d) return -1;   //无解        ll k1,k2;        ex_gcd(M/d,modnum[i]/d,k1,k2);        k1 = ( k1* c/d ) % (modnum[i] / d);        R = R + k1*M;        M = M / d * modnum[i];        R %= M;    }    if( R < 0) R += M;    return R;}//以上所有的mod都要求是质数!!!void solve(){    scanf("%lld%lld%lld",&n,&m,&k);    for(int i=1;i<=k;i++) scanf("%lld",&modnum[i]);    for(int i=1;i<=k;i++) yunum[i] = Lucas(n,m,modnum[i]);    printf("%lld\n",tongyu());}

//总的来说, 如果能想到证明哪里, 那么这道题还是挺好做的!!!

阅读全文
0 0
原创粉丝点击