扩展欧几里得算法
数学推导
现有一二元一次方程:
a
x
1
+
b
y
1
=
m
ax_{1}+by_{1}=m
ax1+by1=m
a
x
2
+
b
y
2
=
g
c
d
(
a
,
b
)
ax_{2}+by_{2}=gcd(a,b)
ax2+by2=gcd(a,b)
由数学中的某一定理可知(裴蜀定理,直接用结论),当二元一次方程的结果
m
m
m是两系数的最大公因数或是最大公因数的倍数,则我们可以得至少有一组整数解可以满足方程。
且 x 1 % x 2 = = y 1 % y 2 = = 0 , x 1 / x 2 = = y 1 / y 2 x_{1}\% x_{2} == y_{1}\% y_{2} == 0\ , x_{1}/ x_{2} == y_{1}/ y_{2} x1%x2==y1%y2==0 ,x1/x2==y1/y2。
现有一算法通过gcd的性质扩展可以求得一组 x 2 , y 2 x_{2} , y_{2} x2,y2。
int exgcd(int a ,int b ,int &x , int &y){
if(!b){
x = 1 , y = 0 ;
return a ;
}
else {
int d = exgcd(b , a%b , y , x) ;
y = y - (a/b) * x ;
return d ;
}
}
同余方程(ACwing878)
题目描述
给定 n 组数据 ai,bi,mi,对于每组数求出一个 x i x_{i} xi,使其满足 a i × x i ≡ b i ( m o d m i ) a_{i}×x_{i}≡b_{i}(mod\ m_{i}) ai×xi≡bi(mod mi) ,如果无解则输出 impossible。
输入格式
第一行包含整数 n n n。
接下来 n n n 行,每行包含一组数据 a i , b i , m i a_{i},b_{i},m_{i} ai,bi,mi。
输出格式
输出共 n n n 行,每组数据输出一个整数表示一个满足条件的 x i x_{i} xi,如果无解则输出 impossible。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在 int 范围之内。
数据范围
1
≤
n
≤
1
0
5
,
1≤n≤10^{5},
1≤n≤105,
1
≤
a
i
,
b
i
≤
2
×
1
0
9
1≤a_{i},b_{i}≤2×10^{9}
1≤ai,bi≤2×109
样例
2
2 3 6
4 3 5
impossible
-3
思路
a
i
×
x
i
≡
b
i
(
m
o
d
m
i
)
a_{i}×x_{i}≡b_{i}(mod\ m_{i})
ai×xi≡bi(mod mi)
可以转化为
a
i
×
x
i
−
m
i
×
y
i
=
b
i
a_{i}×x_{i}-m_{i}\times y_{i} =b_{i}
ai×xi−mi×yi=bi
令
y
i
=
−
y
i
y_{i} = -y_{i}
yi=−yi
得
a
i
×
x
i
+
m
i
×
y
i
=
b
i
a_{i}×x_{i}+m_{i}\times y_{i} =b_{i}
ai×xi+mi×yi=bi
之后便可利用扩展欧几里得解
#include <iostream>
using namespace std ;
typedef long long ll ;
int exgcd(int a , int b , int &x ,int &y){
if(!b){
x = 1 ,y = 0 ;
return a ;
}
int d = exgcd(b , a%b , y , x );
y -= a/b * x ;
return d ;
}
int main(){
int n ;
cin >> n ;
while(n -- ){
int a ,b , m ;
cin >> a >> b >> m ;
int x ,y ;
int d = exgcd(a , m , x , y ) ;
if(b%d) puts("impossible");
else cout << ((ll)x * b / d % m + m) % m << endl ;
}
return 0 ;
}