hihoCoder1303
数论六·模线性方程组
题意:给你一个模线性方程组,求解x
模线性方程组为
x mod m[1] = a[1]
x mod m[2] = a[2]
x mod m[3] = a[3]
......
x mod m[i] = a[i]
其中m[i]为模数,a[i]为余数,需要求解的是x
思路:于hihoCoder#1303 : 数论六·模线性方程组的提示中转载,一些个人见解补充在后面
从n=2开始递推:
已知:
x mod m[1] = a[1]
x mod m[2] = a[2]
根据这两个式子,我们存在两个整数k[1],k[2]:
x = m[1] * k[1] + a[1]
x = m[2] * k[2] + a[2]
由于两个值相等,因此我们有:
m[1] * k[1] + a[1] = m[2] * k[2] + a[2]
=> m[1] * k[1] - m[2] * k[2] = a[2] - a[1]
由于m[1],m[2],r[1],r[2]都是常数,若令A=m[1],B=m[2],C=a[2]-a[1],x=k[1],y=k[2],则上式变为:Ax + By = C。
这可以用欧几里得来求解出x和y
我们可以先通过gcd(m[1], m[2])能否整除r[2]-r[1]来判定是否存在解。
假设存在解,则我们通过扩展欧几里德求解出k[1],k[2]。
再把k[1]代入x = m[1] * k[1] + a[1],就可以求解出x。
同时我们将这个x作为特解,可以扩展出一个解系:
X = x + k*lcm(m[1], m[2]) k为整数
lcm(a,b)表示a和b的最小公倍数。其求解公式为lcm(a,b)=a*b/gcd(a,b)。
将其改变形式为:
X mod lcm(m[1], m[2]) = x。
令M = lcm(m[1], m[2]), R = x,则有新的模方程X mod M = R。
此时,可以发现我们将x mod m[1] = a[1],x mod m[2] = a[2]合并为了一个式子X mod lcm(m[1], m[2]) = x。满足后者的X一定满足前两个式子。
每两个式子都可以通过该方法化简为一个式子。那么我们只要重复进行这个操作,就可以将n个方程组化简为一个方程,并且求出一个最后的解了。
ps:在运用扩展欧几里得求ax+by=gcd(a,b)中的x和y,返回gcd(a,b)
而我们需要求的是
m[1] * k[1] + a[1] = m[2] * k[2] + a[2]
=> m[1] * k[1] - m[2] * k[2] = a[2] - a[1]
由于m[1],m[2],r[1],r[2]都是常数,若令A=m[1],B=m[2],C=a[2]-a[1],x=k[1],y=k[2],则上式变为:Ax + By = C。
扩展欧几里得求的是Ax'+ By' = gcd(A,B);
所以x=x' * C/gcd(A,B)+B/gcd(A,B)*t;
y=y' * C/gcd(A,B)+A/gcd(A,B)*t;
具体代码如下:
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1000000+10;
#define ll long long
//扩展欧几里得求ax+by=gcd(a,b)中的x和y,返回gcd(a,b)
long long extend_gcd(long long a,long long b,long long &x,long long &y) {
if(a == 0 && b == 0)return -1;
if(b ==0 ) {
x = 1;
y = 0;
return a;
}
long long d = extend_gcd(b,a%b,y,x);
y -= a/b*x;
return d;
}
ll m[maxn],a[maxn];
//模数为m,余数为a, 将X%m0=a0和X % m = a整合成一个X%m0=a0等式
bool solve(ll &m0,ll &a0,ll m,ll a) {
long long y,x;
ll g = extend_gcd(m0,m,x,y);
if( abs(a - a0)%g )return false;
x *= (a - a0)/g;
x %= m/g;
a0 = (x*m0 + a0);
m0 *= m/g;
a0 %= m0;
if( a0 < 0 )a0 += m0;
return true;
}
/*
* 无解返回false,有解返回true;
* 解的形式最后为 a0 + m0 * t (0<=a0<m0)
*/
bool MLES(ll &m0 ,ll &a0,int n) { //解为 X = a0 + m0 * k
bool flag = true;
m0 = 1;
a0 = 0;
for(int i = 0; i < n; i++)
if( !solve(m0,a0,m[i],a[i]) ) {
flag = false;
break;
}
return flag;
}
int main() {
int n;
scanf("%d",&n);
for(int i=0; i<n; i++) {
scanf("%lld %lld",&m[i],&a[i]);
}
ll m0,a0;
if(MLES(m0,a0,n))
printf("%lld\n",a0);
else
printf("-1\n");
}