题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5446
C(n, m) % (p1*p2*p3*...*pk)的值
其实这个就是中国剩余定理最后算出结果后的最后一步求余
那C(n, m)相当于以前我们需要用中国剩余定理求的值
然而C(n, m)太大,我们只好先算出
C(n, m) % p1 = r1
C(n, m) % p2 = r2
C(n, m) % p3 = r3
.
.
.
C(n, m) % pk = rk
用Lucas,这些r1,r2,r3...rk可以算出来
然后用中国剩余定理求满足num%p[i]=r[i]的最小num即可,num既是所求答案;
注意在运算过程中会出现数相乘爆long long,所以要手动写乘法求余;
#include <iostream> #include <stdio.h> #include <string.h> #include <string> #include <vector> #include <algorithm> #include <map> #include <queue> #include <stack> #include <math.h> using namespace std; #define met(a, b) memset(a, b, sizeof(a)) #define N 153 #define INF 0x3f3f3f3f typedef long long LL; ///快速幂计算a*b%p,因为a*b直接相乘可能会爆LL; LL Mul(LL a, LL b, LL p) { a = (a+p)%p; b = (b+p)%p; LL ans = 0; while(b) { if(b%2) ans = (ans+a)%p; a=(a+a)%p; b/=2; } return ans; } ///用快速幂求a^b%p; LL quick_mod(LL a, LL b, LL p) { LL ans = 1; a %= p; while(b) { if(b&1) { ans = ans*a%p; b--; } b = b/2; a = a*a%p; } return ans; } ///求C(n, m)%p; LL C(LL n, LL m, LL 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*quick_mod(b, p-2, p)%p)%p; } return ans; } ///卢卡斯用于求C(n, m)%p; LL Lucas(LL n, LL m, LL p) { if(m == 0)return 1; return C(n%p, m%p, p) * Lucas(n/p, m/p, p)%p; } ///扩展欧几里德 求ax+by = gcd(a, b)中的x和y void ex_gcd(LL a, LL b, LL &x, LL &y) { if(b == 0) { x = 1; y = 0; return; } ex_gcd(b, a%b, x, y); int t = x; x = y; y = t - a/b*y; if( a*b < 0 )///当ab异号时; { x = -x; y = -y; } } ///已知 num%p[i]=r[i];求满足n个式子的最小num,其中p[i]是素数; LL China(int n, LL p[], LL r[]) { LL m = 1, num = 0; for(int i=1; i<=n; i++) m *= p[i]; for(int i=1; i<=n; i++) { LL x, y, a = m/p[i], b = -p[i]; ex_gcd(a, b, x, y); LL t = Mul(a, x, m);///Mul就是乘法,防止爆longlong; num = (num + Mul(t, r[i], m) + m) % m; } return (num+m)%m; } int main() { LL n, m; int k, T; scanf("%d", &T); while(T--) { LL p[15], r[15]; scanf("%I64d %I64d %d", &n, &m, &k); for(int i=1; i<=k; i++) { scanf("%I64d", &p[i]); r[i] = Lucas(n, m, p[i]); } printf("%I64d ", China(k, p, r)); } return 0; }