hdu 5446 Unknown Treasure(lucas定理+中國剩餘定理)
阿新 • • 發佈:2019-02-09
題目連結:
解題思路:
首先對於每個質數pi我們,我們可以利用Lucas定理求出Cmn%pi的值,Lucas定理如下:
Cmn%p=Cm/pn/p⋅Cm%pn%p%p
Lucas定理模板如下:
然後我們可以利用中國剩餘定理求取最後答案:
M=∏i=1kpi,Mi=M/pi
Cmn%M=∑i=1kCmn%pi⋅Mi⋅inv[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)這樣一組對應關係。
換句話說,以合數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; }