题意:一个大的组合数模一个大的合数,这个合数可以拆成互不相同的质数。
众所周知,大的组合数模一个素数可以用lucas定理做,模合数,只需要用这个组合数模这个合数的质因数,再用crt反推回去就可以得到答案。
http://blog.csdn.net/acdreamers/article/details/8037918
http://blog.csdn.net/acdreamers/article/details/30992367
这个方法是以前从大神博客里看到的,没想到居然用上了。
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
long long f[100010];
long long m,n,k;
long long p[15],a[15];
long long mod_pow(int a,int n,int p)
{
long long ret=1;
long long A=a;
while(n)
{
if(n&1) ret=(ret*A)%p;
A=(A*A)%p;
n>>=1;
}
return ret;
}
void init(long long p)
{
f[0]=1;
for(int i=1;i<=p;i++)
f[i]=f[i-1]*i%p;
}
long long Lucas(long long a,long long k,long long p)
{
long long re=1;
while(a&&k)
{
long long aa = a%p;long long bb = k%p;
if(aa < bb) return 0;
re = re*f[aa]*mod_pow(f[bb]*f[aa-bb]%p,p-2,p)%p;
a/=p;
k/=p;
}
return re;
}
void ext_gcd(ll a,ll b,ll &d,ll &x,ll &y)
{
if(!b)
{
x=1;y=0;d=a;
return ;
}
else
{
ext_gcd(b,a%b,d,y,x);
y-=a/b*x;
}
}
long long M;///x=a[i](mod m[i])
ll mul(ll x,ll y,ll mod)
{
long long ret=0;
while(y){
if(y&1)ret=(ret+x)%mod;
x=(x+x)%mod;
y>>=1;
}
return ret;
}
long long China(int r)
{
M=1;
for(int i=0;i<r;i++) M*=p[i];
long long ans=0;
for(int i=0;i<r;i++){
long long Mi=M/p[i],d,x0,y0;
ext_gcd(Mi,p[i],d,x0,y0);
ans=(ans+mul(x0,mul(a[i],Mi,M),M))%M;
}
ans=(ans%M+M)%M;
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d%I64d",&m,&n,&k);
for(int i=0;i<k;i++)
{
scanf("%I64d",&p[i]);
init(p[i]);
a[i]=Lucas(m,n,p[i]);
}
long long ans=China(k);
printf("%I64d\n",ans);
}
return 0;
}