简要题意
给定 n n n组非负整数 a i , b i a_i, b_i ai,bi,求解关于 x x x的方程组的最小非负整数解.
每个方程的形式如下:
x ≡ a i ( m o d    b i ) . x\equiv a_i (mod \:\:b_i). x≡ai(modbi).
其中 b 1 , b 2 , b 3 . . . b k b_1,b_2,b_3...b_k b1,b2,b3...bk为不一定两两互质的整数.
拓展中国剩余定理
我们使用数学归纳的方法,假设已经求出前 k − 1 k-1 k−1个方程组成的同余方程组的一个解为 a n s ans ans ,且有 N = l c m ( b 1 , b 2 , . . . , b k − 1 ) N=lcm(b_1,b_2,...,b_{k-1}) N=lcm(b1,b2,...,bk−1),
则前 k − 1 k-1 k−1个方程的方程组通解为 a n s + i ∗ N ( i ∈ Z ) ans+i*N(i\in Z) ans+i∗N(i∈Z) .
那么对于加入第 k k k个方程后的方程组,
我们就是要求一个正整数 t t t,使得 a n s + t ∗ N ≡ a k ( m o d    b k ) ans+t*N \equiv a_k(\mod b_k) ans+t∗N≡ak(modbk),
转化一下上述式子得 t ∗ N ≡ a k − a n s ( m o d    b k ) t*N \equiv a_k-ans(\mod b_k) t∗N≡ak−ans(modbk).
对于这个式子我们已经可以通过扩展欧几里得求解 t t t.
若该同余式无解,则整个方程组无解, 若有,即方程满足 g c d ( a , b ) ∣ a k − a n s gcd(a,b)∣a_k-ans gcd(a,b)∣ak−ans ,则前 k k k个同余式组成的方程组的一个解为 x k = x + t ∗ M x_k=x+t*M xk=x+t∗M,
警告:接下来的步骤可能有些难理解,请动笔跟着思路走.
往后计算取模时就取通解间隔的模了.
(感谢@千年之狐_天才 大佬的帮助)
所以整个算法的思路就是求解k次扩展欧几里得.
程序实现
#include<bits/stdc++.h>
#define ll long long
#define maxn 100010
using namespace std;
int n;
ll a[maxn],b[maxn];
ll xi,yi;
ll fast_mul(ll ai,ll bi,ll mod){
ll ret=0;
while(bi){
if(bi%2)ret=(ret+ai)%mod;
ai=(ai+ai)%mod;
bi/=2;
}
return ret;
}//快速乘,直接乘再取模会慢而且爆long long
ll exgcd(ll ai,ll bi){
if(bi==0){
xi=1,yi=0;
return ai;
}
ll ret=exgcd(bi,ai%bi);
ll zi=xi;
xi=yi;
yi=zi-(ai/bi)*yi;
return ret;
}//拓展欧几里得顺便求gcd
ll excrt(){
ll ans=a[1],N=b[1];
for(int i=2;i<=n;i++){
ll ai=N,bi=b[i],ci=(a[i]-ans%bi+bi)%bi;
ll gcd=exgcd(ai,bi),bg=bi/gcd;//此时算出的xi是对于方程ai*xi+bi*yi=gcd(a,b)的一个可行解,所以接下来还要处理
if(ci%gcd!=0){printf("No answer.");exit(0);}
xi=fast_mul(xi,ci/gcd,bg);
ans+=xi*N;
N*=bg;
ans=(ans%N+N)%N;
}
return (ans%N+N)%N;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&b[i],&a[i]);//注意由于格式的要求,为了满足一般习惯如此输入
printf("%lld\n",excrt());
return 0;
}
u p d   8.8 upd\:8.8 upd8.8可以处理余数都为0的情况.