一道题集合了卢卡斯,中国剩余定理,费马小定理,扩展欧几里德 也是挺厉害的。
卢卡斯求得摸线性方程组,在用中国剩余定理解,会爆long long,用扩展加法
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#define N 100005
#define P 105
using namespace std;
long long pow_mod(long long a, long long b, long long p) {
long long re = 1;
while( b ) {
if( b & 1) {
re = (a * re) % p;
}
a *= a;
a %= p;
b >>= 1;
}
return re;
}
long long fac[N];
void f(long long p){
fac[1] = 1;
fac[0] = 1;
for( long long i=2; i<N; ++i) {
fac[i] = (fac[i-1] * i) % p;
}
}
long long lukas(long long a, long long b, long long p) {
long long re = 1;
while( a && b) {
long long aa = a % p;
long long bb = b % p;
if( aa < bb ){
return 0;
}
re = ((re * fac[aa]) % p) * pow_mod(fac[bb]*fac[aa-bb]%p, p-2, p) % p;
a /= p;
b /= p;
}
return re;
}
long long ext_gcd(long long a, long long b, long long &x, long long &y) {
if(b==0) {
x = 1;
y = 0;
return a;
}
long long r = ext_gcd(b, a%b, x, y);
long long t = x;
x = y;
y = t - a/b*y;
return r;
}
long long mul_mod(long long a, long long b, long long p) {
long long re = 0;
while(b) {
if(b & 1){
re = (re + a) % p;
}
a += a;
a %= p;
b >>= 1;
}
return re;
}
int main(int argc, char* argv[]){
int t;
long long m, n;
long long prime[P];
int p;
scanf("%d", &t);
while(t-- && cin >> m >> n >> p) {
long long pro = 1;
for(int i=0; i<p; ++i) {
cin >> prime[i];
pro *= prime[i];
}
long long re = 0;
for(int i=0; i<p; ++i) {
f(prime[i]);
long long luka = lukas(m, n, prime[i]);
long long x, y;
ext_gcd(pro/prime[i], prime[i], x, y);
x = (x + prime[i]) % prime[i];
re += mul_mod(mul_mod(luka, x, pro), pro / prime[i], pro) % pro;
re %= pro;
}
cout << re << endl;
}
return 0;
}