题目大意:让你求出C(n,m)%M的值。
算法思路:此题的 n和m非常大,因此不能用快速幂取模,这里我们只能用lucas定理,但lucas定理有一个条件,要求C(n,m)%M的M必须要为素数,因此,我们又要用到中国剩余定理。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
int t,k;
LL nn,mm;
LL p[15],fac[100050],inv[100050];
LL pow_mod(LL a, int n, int mod)
{
LL ret = 1;
while (n)
{
if (n&1) ret = ret * a % mod;
a = a * a % mod;
n >>= 1;
}
return ret;
}
void init(int n)
{
fac[0] = 1;
for (int i = 1; i < n; i++) fac[i] = fac[i-1] * i % n;
inv[n-1] = pow_mod(fac[n-1], n-2, n);
for (int i = n - 2; i >= 0; i--) inv[i] = inv[i+1] * (i+1) % n;
}
//lucas定理(用于求C(n,m)%p其中n与m为超大的整数,p是素数)
LL C (int n, int m, int mod)
{
if (m > n || m < 0 || n < 0) return 0;
return fac[n] * inv[m] % mod * inv[n-m] % mod;
}
LL lucas(LL n, LL m, int mod)
{
if (m == 0) return 1;
return lucas(n / mod, m / mod, mod) * C(n % mod, m % mod, mod) % mod;
}
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 mul(LL a, LL b, LL mod) {
a = (a % mod + mod) % mod;
b = (b % mod + mod) % mod;
LL ret = 0;
while(b){
if(b&1){
ret += a;
if(ret >= mod) ret -= mod;
}
b >>= 1;
a <<= 1;
if(a >= mod) a -= mod;
}
return ret;
}
LL china(LL a[],LL m[],int n)
{
LL M = 1;
LL ans = 0;
for(int i=1; i<=n; i++)
M *= m[i];
for(int i=1; i<=n; i++)
{
LL x, y;
LL Mi = M / m[i];
extend_Euclid(Mi, m[i], x, y);
ans = (ans + mul(mul(Mi,x,M),a[i],M));//本来是ans=(ans+Mi*x*a[i])%M;但是在乘的时候有可能会爆LL,因此要按位取摸
}
return (ans+M)%M;
}
int main()
{
LL a[15];
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof(a));
scanf("%lld%lld%d",&nn,&mm,&k);
for(int i=1; i<=k; i++)
{
scanf("%lld",&p[i]);
init(p[i]);
a[i]=lucas(nn,mm,p[i]);
}
LL res=china(a,p,k);
printf("%lld\n",res);
}
}