1. 程式人生 > >hdu 5446 Unknown Treasure(lucas定理+中國剩餘定理)

hdu 5446 Unknown Treasure(lucas定理+中國剩餘定理)

題目連結:

解題思路:

首先對於每個質數pi我們,我們可以利用Lucas定理求出Cmn%pi的值,Lucas定理如下:

Cmn%p=Cm/pn/pCm%pn%p%p

Lucas定理模板如下:

然後我們可以利用中國剩餘定理求取最後答案:

M=i=1kpi,Mi=M/pi

Cmn%M=i=1kCmn%piMiinv[Mi]

因為做乘法過程中可能會超long long ,所以我們要用快速乘法去處理,方便取模 

中國剩餘定理:

我們假設同餘方程組裡所有的a[i]都等於1,並且所有的m[i]都互素。這樣,答案就一定是x恆等於b(mod(m的所有和))。反之,對於一

個合數n,我們假設有n=ab(其中a和b互

素)。那麼如果x mod n的值確定,x mod a 和x mod b的值就都確定了。也就是說,我們有

(x mod n)<=>(x mod a,x mod b)這樣一組對應關係。

換句話說,以合數n為模數來考慮與以a和b為模數來考慮時等價的。這個定理叫做中國剩餘定理。通過對n進行分解,對於模合數的

情況只需要考慮模p的k次方(p為素數)的情況就可以了。其中如果n不能被任何一個完全平方數整除,那麼問題就可以轉化為模數

為素數的情況,從而變得容易求解,中國剩餘定理不是一個演算法,而是可以看成在思考算法時的一個提示。

例:f(x)恆等於0(mod n)<=>f(x)恆等於(mod p的k次方)(p的k次方|n)

AC程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

typedef long long ll;
const int maxn = 20;
ll a[maxn];
ll mod[maxn] ;

ll mul(ll a,ll b,ll mod){
    ll ans = 0;
    while(b){
        if(b&1)
            ans = (ans+a)%mod;
        b >>= 1;
        a = (a+a)%mod;
    }
    return ans;
}

ll quick_mod(ll a,ll b,ll m){
    ll ans = 1;
    a %= m;
    while(b){
        if(b&1)
            ans = ans * a % m;
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

ll getC(ll n, ll m,int cur){
    ll p = mod[cur];
    if(m > n)
        return 0;
    if(m > n-m)
        m = n-m;
    ll ans = 1;
    for(ll i = 1; i <= m; i++){
        ll a = (n + i - m) % p;
        ll b = i % p;
        ans = mul(ans,mul(a,quick_mod(b,p-2,p),p),p);  //p為素數,i對p的逆元可以不用擴充套件歐幾里得進行求解  re=i^(p-2)
    }
    return ans%p;
}

ll Lucas(ll n,ll k,int cur){
    ll p = mod[cur];
    if(k == 0)
        return 1%p;
    return mul(getC(n%p,k%p,cur),Lucas(n/p,k/p,cur),p);
}

void extend_Euclid(ll a, ll b, ll &x, ll &y){
    if(b == 0){
        x = 1;
        y = 0;
        return;
    }
    extend_Euclid(b,a%b,x,y);
    ll tmp = x;
    x = y;
    y = tmp - a / b * y;
}

ll solve(ll a[],ll m[],int k){
    ll M = 1;
    ll ans = 0;
    for(int i=0; i<k; i++)
        M *= mod[i];
    for(int i=0; i<k; i++){
        ll x,y,tmp;
        ll Mi = M / m[i];
        extend_Euclid(Mi, m[i], x, y);
        if(x < 0){
            x=-x;
            tmp = mul(Mi,x,M);
            tmp = mul(tmp,a[i],M);
            tmp = -tmp;
        }
        else {
            tmp = mul(Mi,x,M);
            tmp = mul(tmp,a[i],M);
        }
        ans = (ans + tmp) % M;
    }
    while(ans < 0)
        ans += M;
    return ans;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        ll n,m;
        int k;
        scanf("%lld%lld%d",&n,&m,&k);
        for(int i = 0; i < k; i++)
            scanf("%lld",&mod[i]);
        for(int i = 0;i < k; i++)
            a[i] = Lucas(n,m,i)%mod[i];
        printf("%lld\n",solve(a,mod,k));
    }
    return 0;
}