上一篇博客写的是中国剩余定理模数不互质的情况,然鹅——还存在着模数互质的情况,而原来的做法就没有办法用了
那我们现在该怎么做呢?
原来的思路是对于每一个方程,我们找出一个基础数,使得基础数满足该方程要求。因为是互质的关系,所以所有基础数加起来也不会冲突。(这个由最小公倍数保证)
还是这个栗子 存在一个数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 "); else printf("%d ",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 */