题意:给一个组合数C(n, m) (n, m<1e18)和 mod(mod<1e18),求这个组合数模除mod的值。
思路:先用lucas定理求出组合数模除每个质因子的解,然后用中国剩余定理来进行求解,要注意两个long long类型相乘可能会爆,要把乘法改成加法的形式。
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
typedef long long lint;
lint p[12] ;
lint num[12] ;
LL multi_mod(LL a, LL b, LL c) {
a %= c;
b %= c;
LL ret = 0;
while(b) {
if(b&1) {
ret += a;
if(ret >= c) ret -= c;
}
a <<= 1;
if(a>=c) a-=c;
b >>= 1;
}
return ret;
}
LL quick_mod(LL a, LL p, LL n) {
if(p == 0) return 1;
LL ans = quick_mod(a, p/2, n);
ans = multi_mod(ans, ans, n);
if(p%2 == 1) ans = multi_mod(ans, a, n);
return ans;
}
LL pow_mod(LL a, LL p, LL n) {
if(p == 0) return 1;
LL ans = pow_mod(a, p/2, n);
ans = ans * ans % n;
if(p%2 == 1) ans = ans * a % n;
return ans;
}
LL C(LL n, LL m , lint p)
{
if(m > n) return 0;
LL ans = 1;
for(int i=1; i<=m; i++)
{
LL a = (n + i - m) % p;
LL b = i % p;
ans =ans* (a*pow_mod(b, p-2 , p) %p) %p ;
}
return ans;
}
LL Lucas(LL n, LL m , lint p )
{
if(m == 0) return 1;
//cout << "gan" << endl;
return C(n % p, m % p , p) * Lucas(n / p, m / p , p) % p;
}
lint CRT( lint a[] , lint m[] , int n ){
if(n==1) return num[0];
lint M = 1 ;
for( int i = 0 ; i < n ; i++ ) M = M * m[i] ;
//cout << M << endl ;
lint ret = 0 ;
for( int i = 0 ; i < n ; i++ ){
lint x ;
lint tmp = M / m[i] ;
//cout << m[i] << tmp-2 << tmp << endl ;
x = quick_mod( tmp , m[i]-2, m[i] ) ;
//cout << x << ' ' << tmp << ' ' << M << endl ;
lint y = multi_mod( tmp , x , M ) ;
//cout << x * tmp % M << endl;
y = multi_mod( tmp*x % M, num[i] , M ) ;
ret = ( ret + y ) % M ;
//cout << ret << endl ;
//cout << ret <<endl;
}
return ( ret + M ) % M ;
}
int main()
{
//freopen( "input.txt" , "r" , stdin ) ;
int T;
scanf("%d", &T);
while(T--)
{
lint n , m ;
int t ;
scanf("%I64d%I64d%d", &n, &m, &t);
//cout << quick_mod( 2 , 5 , 7 ) ;
for( int i = 0 ; i < t ; i++ ){
cin >> p[i] ;
num[i] = Lucas( n , m , p[i] ) ;
//cout << num[i] << endl ;
}
lint ans = CRT( num , p , t ) ;
printf("%I64d\n", ans );
}
return 0;
}