中国剩余定理 (CRT)
中国剩余定理,又名孙子定理
能求解什么问题呢?
问题:
一堆物品
3个3个分剩2个
5个5个分剩3个
7个7个分剩2个
问这个物品有多少个
中国剩余定理给出了以下的一元线性同余方程组:
中国剩余定理说明:假设整数m1,m2, ... ,mn两两互质,则对任意的整数:a1,a2, ... ,an,
方程组(S)
有解,并且通解可以用如下方式构造得到:设
是整数m1,m2, ... ,mn的乘积,并设
是除了mi以外的n- 1个整数的乘积。
设
这个就是逆元了
通解形式为
在模M的意义下,方程组(S)只有一个解:
注意这样求解的一定要满足: m1,m2,....,mn 两两互质
1 LL inv(LL t,LL p){ // t 关于 p 的逆元 2 LL x,y,d; 3 d = ex_gcd(t,p,x,y); 4 if (d == 1){ 5 return (x%p+p)%p; 6 }else 7 return -1; 8 } 9 10 11 // n个方程: x = a[i](mod m[i]) (0<=i<n) 12 13 LL china(int n,LL *a,LL *m){ 14 LL M = 1,ret = 0; 15 for (int i=0;i<n;i++){ 16 M *= m[i]; 17 } 18 for (int i=0;i<n;i++){ 19 LL w = M/m[i]; 20 ret = (ret + inv(w,m[i]) * w * a[i]) % M; 21 } 22 return (ret + M) % M; 23 }
但是如果遇到不是互质的模线性方程组我们要怎么办呢?
问题描述:给出bi,ni的值,且n1, n2, n3,…, ni两两之间不一定互质,求Res的值?
解:采用的是合并方程的做法。
这里将以合并第一第二个方程为例进行说明
由上图前2个方程得(设k1、k2为某一整数):
例题: hdu 1573 X问题 【下面已给出代码】
http://acm.hdu.edu.cn/showproblem.php?pid=1573
1 #include <iostream> 2 #include <algorithm> 3 4 typedef long long LL; 5 6 using namespace std; 7 8 int N; 9 10 11 LL ex_gcd(LL a,LL b,LL &x,LL &y){ 12 if (b == 0){ 13 x = 1; 14 y = 0; 15 return a; 16 } 17 LL gcd = ex_gcd(b,a%b,y,x); 18 y -= (a/b)*x; 19 return gcd; 20 } 21 22 LL inv(LL t,LL p){ // t 关于 p 的逆元 23 LL x,y,d; 24 d = ex_gcd(t,p,x,y); 25 if (d == 1){ 26 return (x%p+p)%p; 27 }else 28 return -1; 29 } 30 31 32 // n个方程: x = a[i](mod m[i]) (0<=i<n) 33 34 LL china(int n,LL *a,LL *m){ 35 LL M = 1,ret = 0; 36 for (int i=0;i<n;i++){ 37 M *= m[i]; 38 } 39 for (int i=0;i<n;i++){ 40 LL w = M/m[i]; 41 ret = (ret + inv(w,m[i]) * w * a[i]) % M; 42 } 43 return (ret + M) % M; 44 } 45 46 LL CRT(LL b[],LL n[],int num){ 47 bool flag = false; 48 LL n1 = n[0],n2,b1 = b[0],b2,bb,d,t,k,x,y; 49 for (int i=1;i<num;i++){ 50 n2 = n[i],b2 = b[i]; 51 bb = b2 - b1; 52 d = ex_gcd(n1,n2,x,y); 53 if (bb%d){ 54 flag = true; 55 break; 56 } 57 k = bb / d * x; 58 t = n2 / d; 59 if (t<0) 60 t = -t; 61 k = (k % t + t) % t; 62 b1 = b1 + n1 * k; 63 n1 = n1 / d * n2; 64 } 65 if (flag) //无解 66 return 0; 67 if (b1 == 0) // 如果解为0,题目要求正整数解,显然不可以 68 b1 = n1; // n1为所以n[i] 的最小公倍数 69 if (b1>N) 70 return 0; 71 return (N-b1) / n1 + 1; //形成的解:b1, b1+n1, b1+2n1,..., b1+xni... 72 } 73 74 75 int main(){ 76 int t,num; 77 LL b[10],n[10]; 78 scanf("%d",&t); 79 while (t--){ 80 scanf("%d%d",&N,&num); 81 for (int i=0;i<num;i++){ 82 scanf("%lld",&n[i]); 83 } 84 for (int i=0;i<num;i++){ 85 scanf("%lld",&b[i]); 86 } 87 printf("%lld\n",CRT(b,n,num)); 88 } 89 return 0; 90 }