此处省略引经据典。
(为了方便叙述,x=(a1,a2,a3)(b1,b2,b3)表示 x≡a1(mod b1) x≡a2(mod b2) x≡a3(mod b3) )
举个栗子。
栗子 求x=(2,3,2)(3,5,7)的最小正整数解。
考虑转化为三个子问题。
(显然,对于互质的u,v,可以得到x=(0,0)(u,v)=u*v。因此,(1,0,0)(3,5,7)=(1,0)(3,5*7)=35*(35-1(mod 3))%3)
思考后可得 x=(2*(1,0,0)(3,5,7)+3*(0,1,0)(3,5,7)+2*(0,0,1)(3,5,7))(mod 3*5*7)
推而广之,考虑n个两两互质的数 m1,m2,m3,……mn-1,mn,设他们的积为M。
则,x=(a1,a2,a3,……an)(m1,m2,m3,……mn)=(a1*(1,0,……,0)+a2*(0,1,0,……,0)+……an*(0……0,1))=
(a1*(M/m1)*((M/m1)-1(mod m1)%m1)+……an*(M/mn)*((M/mn)-1(mod mn)%mn))
而关于逆元,可以用扩展欧几里得来求(扩展欧几里得求逆元可以参考小蒟蒻的这篇博客,貌似快速幂求逆元也适用?)。
细节见代码。
1 #include <cstdio> 2 using namespace std; 3 int x,y,M=1; 4 void exgcd(int aa,int bb,int &x,int &y){ 5 if(bb==0){ 6 x=1; 7 y=0; 8 return ; 9 } 10 exgcd(bb,aa%bb,y,x); 11 y-=x*(aa/bb); 12 } 13 const int sz=1e5+5; 14 int a[sz],m[sz],ans,n; 15 int main(){ 16 scanf("%d",&n); 17 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 18 for(int i=1;i<=n;i++)scanf("%d",&m[i]),M*=m[i]; 19 for(int i=1;i<=n;i++){ 20 exgcd(M/m[i],m[i],x,y); 21 x=(x%m[i]+m[i])%m[i]; 22 ans+=a[i]*x*(M/m[i]); 23 ans=(ans%M+M)%M; 24 } 25 printf("%d",ans); 26 return 0; 27 }
引用一个POJ的模板题 POJ1006
该题代码如下
1 #include <cstdio> 2 using namespace std; 3 int x,y; 4 const int M=21252; 5 void exgcd(int aa,int bb,int &x,int &y){ 6 if(bb==0){ 7 x=1; 8 y=0; 9 return ; 10 } 11 exgcd(bb,aa%bb,y,x); 12 y-=x*(aa/bb); 13 } 14 int t,a[3],m[3]={23,28,33},d,ans; 15 int main(){ 16 for(t=1;;++t){ 17 ans=0; 18 for(int i=0;i<3;i++){ 19 scanf("%d",&a[i]); 20 exgcd(M/m[i],m[i],x,y); 21 x=(x%m[i]+m[i])%m[i]; 22 ans+=a[i]*x*(M/m[i]); 23 ans=(ans%M+M)%M; 24 } 25 scanf("%d",&d); 26 if(d==-1)break; 27 ans-=d; 28 ans=(ans%M+M)%M; 29 if(ans==0)ans=M; 30 printf("Case %d: the next triple peak occurs in %d days. ",t,ans); 31 } 32 return 0; 33 }