Files
python/TangDou/AcWing/ExGcd/878.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

3.9 KiB
Raw Blame History

##AcWing 878. 线性同余方程

一、题目描述

给定 n 组数据 $a_i,b_i,m_i$,对于每组数求出一个 $x_i$,使其满足 $a_i×x_i≡b_i(mod \ m_i)$,如果无解则输出 impossible。

输入格式 第一行包含整数 $n$。

接下来 n 行,每行包含一组数据 $a_i,b_i,m_i$。

输出格式 输出共 n 行,每组数据输出一个整数表示一个满足条件的 $x_i$,如果无解则输出 impossible。

每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。

输出答案必须在 int 范围之内。

数据范围 1≤n≤10^5,1≤a_i,b_i,mi≤2×10^9

输入样例:

2
2 3 6
4 3 5

输出样例:

impossible
-3

二、线性同余方程

【扩展欧几里得算法的一个典型应用】

ax\equiv\ b(mod\ m)

举个栗子: 2x \equiv 3 (mod\ 6)


x=1,2x=2,2\%6=2
x=2,2x=4,4\%6=4
x=3,2x=6,6\%6=0


x=4,2x=8,8\%6=2 x=5,2x=10,10\%6=4 x=6,2x=12,12\%6=0


... 余数开始循环了,一直都是2,4,0...

不会产生$3$这个余数,所以,无解。

再来一个栗子:

4x\equiv\ 3 (mod\ 5)

x=1,4x=4, 4\%5 =4 $x=2,4x=8, 8%5 =3$ x=3,4x=12, 12\%5 =2 x=4,4x=16, 16\%5 =1 x=5,4x=20, 20\%5 =0


x=6,4x=24, 24\%5 =4 $x=7,4x=28, 28%5 =3$ x=8,4x=32, 32\%5 =2 x=9,4x=36, 36\%5 =1 x=10,4x=40, 40\%5 =0


.... 所以有解,其它第一个解是 x=2,第二个解是$7$。 这两个解之间差了一个$5$,即$2+5=7$,每加一个$5$,都可以得到一个解。比如 $7+5=12$也是一个解。

2、为什么线性同余方程可以使用欧几里得算法来求解呢?

  • a∗x\equiv b (mod\ m) 表示意义 是(a*x) \% m=b,从而(a∗x−b)\%m=0 。

假设是$y_1$倍,因此线性同余方程转化为 a∗x-b=m∗y_1 , 移项: $ax-my_1=b$。令 -y_1=y ,则: a * x + m * y = b ,这就是一个标准的二元一次方程。

  • 根据贝祖定理,上述等式有解当且仅当 $gcd(a,m)|b$,也就是$b$是$gcd(a,m)$的整数倍,有解; 如果$b$不是$gcd(a,m)$的整数倍,无解,输出 impossible

  • 用扩展欧几里得求出一组特解 x_0,$y_0$, 使得 $a∗x_0+m∗y_0=gcd(a,m)$。

  • 对比两个方程:扩展欧几里得公式和线性同余方程两个东东

$ \large \left{\begin{matrix} ax + my = b & \ a∗x_0+m∗y_0=gcd(a,m) & \end{matrix}\right. $

两个方程的左右两边,左边的系数是一样的a,m,右边的常数不一样,一个是b,另一个是$gcd(a,m)$,我们刚才上面讨论过,$b$必须是$gcd(a,m)$的整数倍,方程才有解,也就是说,现在$b$一定是$gcd(a,m)$的整数倍!

设 d=gcd(a,m),那么b=t*d,其中$t=b/gcd(a,m)$是一个整数。

\therefore x=b/gcd(a,m) * x_0,

因为害怕在乘法运算过程中,造成爆int,所以,出发前对$b$进行了转$long\ long$操作,以保证不爆int.同时,因为是同余方程,如果取得的值大于m,还需要模一下m.即:

(LL) b / d * x % m

三、实现代码

#include <bits/stdc++.h>
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
            printf("%d\n", (LL)b / d * x % m);
    }
    return 0;
}