[Acwing] 878. 线性同余方程

题意

求出一个 x i x_i xi满足 a i ∗ x i ≡ b i ( m o m i ) a_i*x_i\equiv b_i(mo m_i) aixibi(momi)

思路

( m ∣ x m|x mx表示 m m m x x x的因子,或者 x x x能被 m m m整除)

首先什么是同余 ?

设整数 m ≠ 0 m\neq0 m=0

m ∣ ( a − b ) m|(a-b) m(ab),称 m m m位模数, a a a同余 b b b m m m, b b b a a a对模 m m m的剩余记作 a ≡ b ( m o d   m ) a\equiv b(mod\ m) ab(mod m)

则我们可以知道 a − b a-b ab m m m的倍数,因此上面的式子可以化为
a ∗ x + y ∗ m = b \begin{aligned} &a*x+y*m=b\\ \end{aligned} ax+ym=b

但是显然这里并不能使用 扩 展 欧 几 里 得 扩展欧几里得

因为扩展欧几里得需要满足
a ∗ x + y ∗ m = g c d ( a , m ) \begin{aligned} &a*x+y*m=gcd(a,m)\\ \end{aligned} ax+ym=gcd(a,m)

那么上面那个定理什么时候有解呢 ?

    a x + b y = c 设 d = g c d ( a , b )         d ∣ a , d ∣ b     d ∣ ( a x ) , d ∣ ( b y ) c = m ∗ d = m ∗ g c d ( a , b ) \begin{aligned} &\ \ \ ax+by=c\\ &设d=gcd(a,b)\\ &\ \ \ \ \ \ \ d|a,d|b\\ &\ \ \ d|(ax),d|(by)\\ &c=m*d=m*gcd(a,b) \end{aligned}    ax+by=cd=gcd(a,b)       da,db   d(ax),d(by)c=md=mgcd(a,b)
则显然 c c c g c d ( a , b ) gcd(a,b) gcd(a,b)的时候有解,这就是贝祖定理

因此我们又回去考虑
a ∗ x + y ∗ m = b \begin{aligned} &a*x+y*m=b\\ \end{aligned} ax+ym=b

显然当 g c d ( a , m ) ∣ b gcd(a,m)|b gcd(a,m)b的时候有解

因此我们不妨先用扩展欧几里得求解出
a ∗ x 0 + y 0 ∗ m = g c d ( a , m ) \begin{aligned} &a*x_0+y_0*m=gcd(a,m)\\ \end{aligned} ax0+y0m=gcd(a,m)

然后我们将式子左右两边同时乘上 b g c d ( a , m ) \frac{b}{gcd(a,m)} gcd(a,m)b
( a ∗ x 0 + y 0 ∗ m ) ∗ b g c d ( a , m ) = g c d ( a , m ) ∗ b g c d ( a , m ) a ∗ x 0 ∗ b g c d ( a , m ) + y 0 ∗ b g c d ( a , m ) = b \begin{aligned} &(a*x_0+y_0*m)*\frac{b}{gcd(a,m)}=gcd(a,m)*\frac{b}{gcd(a,m)}\\ &a*x_0*\frac{b}{gcd(a,m)}+y_0*\frac{b}{gcd(a,m)}=b \end{aligned} (ax0+y0m)gcd(a,m)b=gcd(a,m)gcd(a,m)bax0gcd(a,m)b+y0gcd(a,m)b=b

因此最后对于上面由扩展欧几里得求出的 x 0 x_0 x0 可以用来推导出所求式的 x x x

x = x 0 ∗ g c d ( a , m ) / b % m x = x_0*gcd(a,m)/b\%m x=x0gcd(a,m)/b%m

Code :

#include <iostream>
#include <algorithm>

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;
    scanf("%d", &n);
    while (n -- )
    {
        int a, b, m;
        scanf("%d%d%d", &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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值