对于 a ∗ x + b ∗ y = c a*x+b*y=c a∗x+b∗y=c,这样一个二元一次方程组,我们想要得到他的一组解可以用扩展欧几里得算法,参数列表的a,b,x,y就是方程中的a,b,x,y,d计算出来是gcd(a,b)。
算法求出来的是 a ∗ x + b ∗ y = g c d ( a , b a*x+b*y=gcd(a,b a∗x+b∗y=gcd(a,b的一组解,解系可以表示为 X = x + b / d ∗ k , Y = y − a / d ∗ k X=x+b/d*k,Y=y-a/d*k X=x+b/d∗k,Y=y−a/d∗k,k为任意整数
但是我们要求解的是 a ∗ x + b ∗ y = c a*x+b*y=c a∗x+b∗y=c
方程组有解的充分必要条件是 d ∣ c d|c d∣c,正确性很容易证明
在确定方程组优解以后 c / d ∗ X , c / d ∗ Y c/d*X,c/d*Y c/d∗X,c/d∗Y为方程组的一组解系
需要注意的是这样得到的不是方程组的所有的解
例如:对于方程 3 x + y = 60 3x+y=60 3x+y=60, 17 ∗ 3 + 9 ∗ 1 = 60 17*3+9*1=60 17∗3+9∗1=60显然为一组解,但是我们却没有办法用过扩展欧几里得算法得到这组解。所以凡是对x,y有限制条件(不是指正负,而是要求和不能超过多少等条件)我们要考虑是否能够使用扩展欧几里得算法,相反,如果对x,y具体大小没有过多限制,而是仅仅说明要求正负等条件时就可以使用扩展欧几里得算法。
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y)
{
if(!b){d=a; x=1; y=0;}
else {gcd(b,a%b,d,y,x); y-=x*(a/b);}
}
下面简单证明一下正确性。
令c=a%b=a-k*b (k=a/b)
a
∗
x
1
+
b
∗
x
2
=
g
c
d
(
a
,
b
)
a*x1+b*x2=gcd(a,b)
a∗x1+b∗x2=gcd(a,b)
b
∗
x
2
+
c
∗
y
2
=
g
c
d
(
b
,
c
)
b*x2+c*y2=gcd(b,c)
b∗x2+c∗y2=gcd(b,c)
有最大公因数的性质我们知道
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
c
)
gcd(a,b)=gcd(b,c)
gcd(a,b)=gcd(b,c)
所以我们得到等式
a
∗
x
1
+
b
∗
y
1
=
b
∗
x
2
+
c
∗
y
2
=
b
∗
x
2
+
a
∗
y
2
−
k
∗
b
∗
y
2
a*x1+b*y1=b*x2+c*y2=b*x2+a*y2-k*b*y2
a∗x1+b∗y1=b∗x2+c∗y2=b∗x2+a∗y2−k∗b∗y2
其中的一组解为
x
1
=
y
2
,
y
1
=
x
2
−
k
∗
y
2
,
其
中
k
=
a
/
b
x1=y2,y1=x2-k*y2,其中k=a/b
x1=y2,y1=x2−k∗y2,其中k=a/b,因此我们可以递归的求解,终止递归的条件为c==0,则这个时候的b为gcd(a,b),解为x=1,y=0,然后递归的返回(这个时候再看代码应该就能理解了,代码实现很巧妙)
为了快速得到非负的X,可以稍微处理一下:X=(X%B’+B’)%B’,这样得到的就应该是最小的非负X了
(B’=B/D)
简单的例题hihoCoder#1297
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<ctime>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN=1e5+5;
ll s1,s2,v1,v2,m;
void ex_gcd(ll A,ll B,ll& D,ll& x,ll& y)
{
if(!B)
{
D=A; x=1; y=0;
}
else
{
ex_gcd(B,A%B,D,y,x);
y-=(A/B)*x;
}
}
int main()
{
while(~scanf("%lld%lld%lld%lld%lld",&s1,&s2,&v1,&v2,&m))
{
ll A=v1-v2,B=m,C=s2-s1,D,x,y;
if(A<0)
{
A=-A; C=-C;
}
C=(C%m+m)%m;
ex_gcd(A,B,D,x,y);
if(C%D)
{
printf("-1\n");
continue;
}
B/=D;
x=C/D*x;
x=(x%B+B)%B;
printf("%lld\n",x);
}
return 0;
}