线性同余方程(扩展欧几里得应用)
题目内容
给定\(n\)组数据\(a_i, b_i, m_i\), 对于每组数据求出一个\(x_i\),使其满足\(a_i \times x_i \equiv b_i(mod \ m_i)\)
如果无解则输出impossible
输入格式
第一行包含整数 \(n\),接下来\(n\)行,每行包含一组数据$ a_i, b_i, m_i$
输出格式
输出共\(n\)行, 每行数据输出一个整数表示一个满足条件的 \(x_i\),如果无解则输出 impossible
。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在\(int\) 范围之内
数据范围
\(1 \le n \le 10^5\)
\(1 \le a_i,b_i,m_i \le 2 \times 10^9\)
输入样例
2
2 3 6
4 3 5
输出样例
impossible
-3
一、问题解决
-
把 \(ax \equiv b \ \ (mod \ m)\)表示\(ax - b\)为\(m\)的倍数, 等价为 \(ax = my + b\) , 变形可得 \(ax-my=b\) .
-
令\(y' = -y\),上式变为\(ax + my' = b\),即线性同余方程方程等价为\(ax + my = b\),
-
判断是否有解(详见第二部分是否有解)
-
运用欧几里得算法求出一组解\(x_0, y_0\), 使得\(ax_0 + my_0 = gcd(a,m)\) ,但题目本身是求\(ax + my = b\),所以两边同时乘以\(\frac{b}{gcd(a,m)}\)即可,即\(x = x_0 \times \frac{b}{gcd(a,b)} \% \ m\)
二、是否有解
引入一个定理:裴蜀定理(贝祖定理):
设\(a,b\)不全为零的整数,则存在整数\(x, y\)使得\(ax + by = gcd(a, b)\)
关于定理的证明
: https://oi-wiki.org/math/number-theory/bezouts/
所以由裴蜀定理可知:
-
当\(gcd(a,m) \ | \ b\)时,方程有解
整除的解释:'|' 整除号,如a|b (a不为0),若存在整数k,使得b=ka,则称a整除b
-
反之,方程无解
三、扩展欧几里得算法
用于求解\(ax+by=gcd(a,b)\)的解
-
当\(b = 0\)时,\(ax = a\),此时\(x = 1, y = 0\)
注意: 当a不为0时,gcd(a, 0) = gcd(0, a) = a
-
当\(b \neq 0\)时,由于\(gcd(a, b) = gcd(b, a\ \% \ b)\)
代入\(ax + by = gcd(a, b)\)中可联立方程
\[\begin{aligned} & ax + by = gcd(a,b) \qquad \qquad \qquad①\\ & bx' + (a \ \% \ b)y' = gcd(b, a\ \% \ b)\ \quad② \end{aligned} \]因为\(a \% b = a - \lfloor {a/b}\rfloor \times b\),代入整理可得
\(ay' + b(x' - \lfloor a/b \rfloor \times y') = gcd(b,\ a \% \ b) = gcd(a, b)\)
所以:
\[x = y',y = x' - \lfloor a/b \rfloor \times y' \quad ③ \]
所以可以利用递归算法,求出下一层的\(x' 与 y'\),递归至边界\(b = 0\)时,\(x = 1, y = 0\)
,然后再利用公式③回代即可求解
五、C++代码
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
int n, x, y;
int exgcd(int a, int b, int &x, int &y){//ax + by = gcd(a, b)
if(b == 0){
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, x, y);
int z = x;
x = y;
y = z - a / b * y;
return d;
}
int main(){
cin >> n;
while(n --){
int a, b, m;
cin >> a >> b >> m;
//ax + my = b,传入参数要匹配
int d = exgcd(a, m, x, y);
if(b % d) cout << "impossible\n";
else{
x = (LL)x * b / d % m;
cout << x << endl;
}
}
return 0;
}