题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5446
解题思路:
首先对于每个质数pi我们,我们可以利用Lucas定理求出
Cmn%pi
的值,Lucas定理如下:
Cmn%p=Cm/pn/p⋅Cm%pn%p%p
Lucas定理模板如下:
http://blog.csdn.net/piaocoder/article/details/48445215
然后我们可以利用中国剩余定理求取最后答案:
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的值就都确定了。也就是说,我们有
(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;
}