线性同余方程(扩展欧几里得应用)

题目内容

给定\(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

一、问题解决

  1. \(ax \equiv b \ \ (mod \ m)\)表示\(ax - b\)\(m\)的倍数, 等价为 \(ax = my + b\) , 变形可得 \(ax-my=b\) .

  2. \(y' = -y\),上式变为\(ax + my' = b\),即线性同余方程方程等价为\(ax + my = b\)

  3. 判断是否有解(详见第二部分是否有解)

  4. 运用欧几里得算法求出一组解\(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/

所以由裴蜀定理可知:

  1. \(gcd(a,m) \ | \ b\)时,方程有解

    整除的解释:'|' 整除号,如a|b (a不为0),若存在整数k,使得b=ka,则称a整除b

  2. 反之,方程无解

三、扩展欧几里得算法

用于求解\(ax+by=gcd(a,b)\)的解

  1. \(b = 0\)时,\(ax = a\),此时\(x = 1, y = 0\)

    注意: 当a不为0时,gcd(a, 0) = gcd(0, a) = a

  2. \(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;
}
posted @ 2022-06-21 16:24  Maple1234  阅读(62)  评论(0编辑  收藏  举报