AcWing 878. 线性同余方程

题目传送门

一、理解与感悟

1、线性同余方程 【扩展欧几里得算法的一个典型应用】

(axequiv 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;)
...
不会产生(3)这个余数,所以,无解。

(4xequiv 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=2),第二个解是(7)
这两个解之间差了一个(5),即(2+5=7),每加一个(5),都可以得到一个解。比如 (7+5=12)也是一个解。

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

(1) (a∗xequiv b (mod m))表示的意义就是(a*x \% m=b),从而((a∗x−b)\%m=0)

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

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

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

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

(a*x + m*y = b)

(a∗x_0+m∗y_0=gcd(a,m))

两个方程的左右两边,左边的系数是一样的(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))是一个整数。

( herefore 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() {
    //优化输入
    ios::sync_with_stdio(false);
    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
", (LL) b / d * x % m);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/littlehb/p/15375247.html