上一篇博客寫的是中國剩余定理模數不互質的情況,然鵝——還存在着模數互質的情況,而原來的做法就沒有辦法用了
那我們現在該怎么做呢?
原來的思路是對於每一個方程,我們找出一個基礎數,使得基礎數滿足該方程要求。因為是互質的關系,所以所有基礎數加起來也不會沖突。(這個由最小公倍數保證)
還是這個栗子 存在一個數x,除以3余2,除以5余3,除以7余2,然后求這個數
3的基礎數是35,滿足除以3余2,而且是5和7的倍數,5,7同理。也就是說:基礎數幾倍幾倍的變化是不會造成突然冒出來一個什么數%5或%7出現了余數
而現在模數不互質,還是舉個栗子存在一個數x,除以6余4,除以8余2,除以9余7,然后求這個數
答案手推出來是34,如果仍然按照原來的做法lcm為72,6的基礎數為。。。誒誒?怎么推不出來?72/6=12,ok,12不滿足,再擴大,emm,還是不滿足,再擴大。。。
好吧,相信大家已經發現了!12是6的倍數啊!再怎么擴大也不可能滿足條件啊!
這就是互質與不互質的區別:不互質可能會導致基礎數是模數的倍數,那么這個算法就涼涼了
那就讓我們換一個思維方式吧!
可以先對第一個方程求解出一個滿足條件的x,再去看下一個方程,在這個x的基礎上,加上一個滿足下一個方程的x',同時不破壞前面方程的要求。
也就是說,我們去判斷一個方程有沒有解,解是多少。這,這不就是擴歐嗎?
具體看代碼吧~
#include<bits/stdc++.h> using namespace std; int a[10],m[10],tong[103]; int exgcd(int a,int &x,int b,int &y) { if(b==0){x=1;y=0;return a;} int x2,y2; int gcd=exgcd(b,x2,a%b,y2); x=y2; y=x2-a/b*y2; return gcd; } int main() { freopen("crt.in","r",stdin); freopen("crt.out","w",stdout); int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); scanf("%d%d",&a[1],&m[1]); //用lcm求當前到達過的方程中,所有模數的lcm,sum是最后的結果,但是每一次都會用 //解出的x去更新它,fail判斷是否有解 int lcm=m[1],sum=a[1],fail=0; for(int i=2;i<=n;i++) { int x,y; scanf("%d%d",&a[i],&m[i]); a[i]=((a[i]-sum)%m[i]+m[i])%m[i];//sum就是要求的x,不斷更新 int d=exgcd(lcm,x,m[i],y);//lcm*x+m[i]*y+sum=a[i]才能保證x%m[i]=a[i] if(a[i]%d==0) x=x*(a[i]/d)%m[i]; else fail=1; sum+=x*lcm;//注意是乘lcm哦!不然可能會出現前面的方程沖突 lcm=lcm/d*m[i];//到現在這一個方程了,所有模數的最小公倍數 sum=(sum%lcm+lcm)%lcm; } if(fail) printf("No\n"); else printf("%d\n",sum); } } /* 3 4 6 2 8 7 9 3 2 3 5 4 7 3 4 6 2 8 7 9 2 1 2 2 4 */