AcWing 878. 线性同余方程

\(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\) , 移项: \(a*x-m*y_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} a*x + m*y = 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;
}

posted @ 2021-10-07 10:31  糖豆爸爸  阅读(300)  评论(0编辑  收藏  举报
Live2D