扩展欧几里得模板与个人见解

 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");
}

猜你喜欢

转载自blog.csdn.net/qq_40160605/article/details/81090225