题目大意:给k个素数pi,记M为其乘积。计算C(n,m)%M。
1、令a[i]=C(n,m)%pi,可用Lucas定理进行计算。
2、问题转化为求最小的x,使得x%pi=ai。根据CRT求解即可。
3、注意在应用CRT的时候,中间结果会溢出,需要用到快速乘法。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef __int64 LL;
#define maxn 10
LL mul(LL a,LL b,LL p)
{
LL ret=0;
while(b)
{
if(b&1) ret=(ret+a)%p;
a=(a+a)%p;
b>>=1;
}
return ret;
}
LL exgcd(LL a,LL b,LL &x,LL &y) //ax+by=d
{
LL d;
if(!b) {x=1;y=0;return a;}
d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
LL CRT(LL a[],LL m[],LL len) //x%m[i]=a[i]
{
LL i,x,y,M,n=1,ret=0;
for(i=0;i<len;++i) n*=m[i];
for(i=0;i<len;++i){
M=n/m[i];
exgcd(M,m[i],x,y);
ret=(ret+mul(mul(x,M,n),a[i],n))%n;
}
return (ret+n)%n;
}
LL pow(LL a,LL b,LL p)
{
LL ans=1;
while(b)
{
if(b&1) ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
LL C(LL n,LL m,LL p)//组合数模素数P
{
if(m>n||m<0) return 0;
if(n-m<m) m=n-m;
LL a=1,b=1;
for(int i=0;i<m;++i)
{
a=a*(n-i)%p;
b=b*(m-i)%p;
}
return a*pow(b,p-2,p)%p;
}
LL Lucas(LL n,LL m,LL p)
{
LL ans=1;
while(n&&m&&ans)
{
ans=ans*C(n%p,m%p,p)%p;
n/=p,m/=p;
}
return ans;
}
LL a[maxn],p[maxn];
int main (){
int T,k,i;
LL n,m;
scanf("%d",&T);
while(T--)
{
scanf("%I64d%I64d%d",&n,&m,&k);
for(i=0;i<k;++i){
scanf("%I64d",&p[i]);
a[i]=Lucas(n,m,p[i]);
}
printf("%I64d\n",CRT(a,p,k));
}
return 0;
}