题目链接:https://www.acwing.com/problem/content/206/
给定2n个整数a1,a2,…,an和m1,m2,…,mn,求一个最小的非负整数x,
满足∀i∈[1,n],x≡mi(mod ai)。
输入格式
第1行包含整数n。
第2..n行:每i+1行包含两个整数ai和mi,数之间用空格隔开。
输出格式
输出最小非负整数x,如果x不存在,则输出-1。
如果存在x,则数据保证x一定在64位整数范围内。
数据范围
1≤ai≤2^31−1,
0≤mi<ai
1≤n≤25
输入样例:
2
8 7
11 9
输出样例:
31
题解:
参考李煜东大佬的算法竞赛进阶指南。
本题中的mi不一定两两互质,中国剩余定理不再适用。可以考虑使用数学归纳法,假设已经求出了前k-1个方程构成的方程组的一个解x。记m=lcm(m1,m2,,,,mk-1),则x+i*m 是k-1个方程的通解。
考虑第k个方程,求出一个整数t,使得x+t*m≡ak(mod mk)。该方程等价于m*t≡ak-x(mod mk),其中t是未知量。这就是一个线性同余方程,可以使用扩展欧几里得算法判断是否有解,并求出它的解。若有解,则x'=x+t*m就是前k个方程构成的方程组的一个解。
综上所述,使用n次扩展欧几里得算法,就求出了整个方程的解 。
扩展欧几里得算法能算出ax+by=gcd(a,b)的一个特解,对于ax+by=c. 设 d=gcd(a,b). 如果 c%d==0 有解
通解是x’=c/d * x+k*(b/d) 另t=b/d,最小正整数解为x=(x*c/d%t+t)%t;
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50;
ll a[maxn],m[maxn];
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b) {x=1,y=0;return a;}
ll d=exgcd(b,a%b,x,y);
ll z=x;x=y;y=z-y*(a/b);
return d;
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i]>>m[i];
ll d,x,y;
bool flag=true;
for(int i=1;i<n;i++){
ll c=m[i]-m[i-1];
d=exgcd(a[i-1],a[i],x,y);
if(c%d!=0){
flag=false;
break;
}
ll t=a[i]/d;
x=(x*(c/d)%t+t)%t;
m[i]=a[i-1]*x+m[i-1];
a[i]=a[i-1]*(a[i]/d);
}
if(!flag) puts("-1");
else printf("%lld\n",m[n-1]);
return 0;
}