求C(n,m)mod p,其中 ,
是素数。
因为n,m,p的数量级很大,直接用递推式会暴。
lucas定理可以求解大组合数取模,即可以利用lucas定理对每个素数取模求余数之后,再用中国剩余定理求解最小满足解。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll e_gcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1;y=0;
return a;
}
ll d=e_gcd(b,a%b,y,x);
y-=(a/b)*x;
return d;
}
ll quick_multi(ll a,ll b,ll mod){
ll res=1;
while(b){
if(b&1) res=(res*a)%mod;
b>>=1;
a=(a*a)%mod;
}
return res;
}
ll C(ll n,ll m,ll mod){
if(n<m) return 0;
if(n==m) return 1;
if(m>(n-m)) m=n-m;
ll fz=1,fm=1;
for(int i=1;i<=m;i++){
fz=fz*i%mod;
fm=fm*(n-i+1)%mod;
}
ll res=(fm*quick_multi(fz,mod-2,mod))%mod;
return res;
}
ll lucas(ll n,ll m,ll p){
if(m==0) return 1;
ll res=lucas(n/p,m/p,p)*C(n%p,m%p,p);
return res;
}
ll multi(ll a,ll b,ll mod){
ll res=0;
while(b){
if(b&1) res=(res+a)%mod;
b>>=1;
a=(a+a)%mod;
}
return res;
}
ll china(ll pri[],ll a[],ll len){
ll M=1,res=0;
for(int i=0;i<len;i++) M*=pri[i];
for(int i=0;i<len;i++){
ll m=M/pri[i];
ll x,y;
e_gcd(pri[i],m,x,y);
res=(res+multi(multi(y,m,M),a[i],M))%M;
}
return (res%M+M)%M;
}
ll pri[20],a[20];
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("%d",&pri[i]);
a[i]=lucas(n,m,pri[i]);
}
ll ans=china(pri,a,k);
printf("%lld\n",ans);
}
return 0;
}