1. 程式人生 > >hdu 5446 lucas+crt+按位乘

hdu 5446 lucas+crt+按位乘

我們 hdu exgcd using sdn pac log mes end

http://acm.hdu.edu.cn/showproblem.php?pid=5446

題意:題目意思很簡單,要你求C(n,m)mod p的值 p=p1*p2*...pn;

題解:對於C(n,m)mod p 由於n,m的值很大 我們用lucas定理把n,m的範圍縮小。由於模數是由若幹個素數的乘積組成,那麽對於最終要求的解x,我們可以用中國剩余定理求解。中國剩余定理如下:

設正整數技術分享兩兩互素,則同余方程組

技術分享

有整數解。並且在模技術分享下的解是唯一的,解為

技術分享

其中技術分享,而技術分享技術分享技術分享的逆元。

最後說一點,由於數據的範圍還是比較大,在乘法求解的過程中,如果用普通的乘法,是會溢出的,這裏還要用到按位乘法(具體看代#include <cstdio>#include <iostream>

#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
ll m[100],a[100];
ll mul(ll a,ll b,ll p)// 按位乘
{
    ll ret=0;
    while(b)
    {
        if(b&1) ret=(ret+a)%p;
        b
=b>>1; a=(a+a)%p; } return ret; } ll exgcd(ll a,ll b,ll &x,ll &y)// 擴展歐幾裏得 { if(b==0) { x=1; y=0; return a; } ll temp=exgcd(b,a%b,y,x); y-=(a/b)*x; return temp; } ll finv(ll a,ll m)// 求逆元 { ll x,y; ll g=exgcd(a,m,x,y); x
=(x%m+m)%m;// return x; } ll c(ll n,ll m,ll p) { if(m > n) return 0; ll a,b; a=b=1; while(m) { a=(a*n)%p; b=b*m%p; n--; m--; } return mul(a,finv(b,p),p); } ll lucas(ll n,ll m,int p) { if(m==0) return 1;// c(n,0)=1; return mul(lucas(n/p,m/p,p),c(n%p,m%p,p),p);// lucas把組合數要求解的範圍縮小到了p之內 } ll crt(int len) { ll sum=0; ll M=1; for(int i=1;i<=len;i++) M*=m[i]; for(int i=1;i<=len;i++) { ll temp=M/m[i]; sum=(sum+mul(mul(a[i],temp,M),finv(temp,m[i]),M))%M;// 這裏有一個數據溢出的問題 對於相乘數據會溢出的問題 用轉為二進制的按位乘法 } return sum; } void init(ll p) { fac[0]=1; fac[1]=1; for(ll i=2;i<=p;i++) fac[i]=fac[i]*i%p; } int main() { cin.sync_with_stdio(false); int t; cin>>t; while(t--) { ll n,mm,k; cin>>n>>mm>>k; init(k); for(int i=1;i<=k;i++) { cin>>m[i]; a[i]=lucas(n,mm,m[i]); } cout<<crt(k)<<endl; } return 0; }

hdu 5446 lucas+crt+按位乘