线性同余方程

欧几里得算法

原理

欧几里得算法用于求解两个数的最大公约数,它基于下面这个公式:

\[\gcd(a,b)=\gcd(b,a\bmod b) \]

可以简单地证明:

\(A\)\(a,b\) 的公约数组成的集合, \(B\)\(b,a\bmod b\) 的公约数组成的集合,只需证 \(\max\{A\}=\max\{B\}\)

  • \(m\in A\) ,则 \(\exist k_1,k_2\text{ s.t. }m\times k_1=a,m\times k_2=b\)

    \(a \bmod b=a-b\lfloor\frac{a}{b}\rfloor=m(k1-k_2\lfloor\frac{k_1}{k_2}\rfloor)\)

    所以 \(m\in B\)

  • \(m\in B\) ,则 \(\exist k_1,k_2\text{ s.t. }m\times k_1=b,m\times k_2=a \bmod b\)

    \(a=a\bmod b+b\lfloor\frac{a}{b}\rfloor=m(k_2+k_1\lfloor\frac{a}{b}\rfloor)\)

    所以 \(m\in A\)

综上, \(A=B,\max\{A\}=\max\{B\}\)

模板

int gcd(int a, int b)
{
    return a % b == 0 ? b : gcd(b, a % b);
}

扩展欧几里得算法

原理

扩展欧几里得算法在求出最大公约数的的同时还能求出不定方程 \(ax+by=\gcd(a,b)\) 的一组解

\[\begin{cases} \gcd(a,b)=\gcd(b,a\bmod b)\\ ax_1+by_1=\gcd(a,b)\\ bx_2+(a \bmod b)y_2=\gcd(b,a\bmod b) \end{cases} \]

可以推出:

\[\Rightarrow ay_2+b(x_2-y_2\lfloor\frac{a}{b}\rfloor)=ax_1+by_1 \]

所以有:

\[\begin{cases} x_1=y_2\\ y_1=x_2-y_2\lfloor\frac{a}{b}\rfloor \end{cases} \]

所以只要求出 \(x_2,y_2\) 就能得到 \(x_1,y_1\)

模板

int exgcd(int a, int b, int & x, int & y)
{
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    int d = exgcd(b, a % b, x, y);
    int t = x;
    x = y;
    y = t - y * a / b;
    return d;
}

还有一种更简洁的版本,在递归调用时直接交换 \(x,y\)

int exgcd(int a, int b, int & x, int & y)
{
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

线性同余方程

线性同余方程指的是形如 \(ax\equiv c\pmod b\) 的方程,它等价于方程 \(ax+by=c\)有解的充要条件为:

\[\gcd(a,b)\mid c \]

所以可以先用扩展欧几里得算法求出 \(ax+by=\gcd(a,b)\) 的一组解 \((x_0,y_0)\) ,那么原方程的一组解就是:

\[\begin{cases} x_1=\frac{cx_0}{\gcd(a,b)}\\ y_1=\frac{cy_0}{\gcd(a,b)}) \end{cases} \]

\(a_1\gcd(a,b)=a,b_1\gcd(a,b)=b\) ,则通解可以表示为:

\[\begin{cases} x = x_1+b_1t\\ y = y_1 - a_1t \end{cases} \]

其中 \(t\in\mathbb{Z}\) ,由 \(a_1 b=ab_1\) ,将通解带回原式,很容易就能验证其正确性

最小的正整数解为:

\[x=(x\bmod b_1+b_1)\bmod b_1 \]

\(x,y\) 都为正时,有如下不等式:

\[\frac{-x_1}{b_1}< t<\frac{y_1}{a_1}\\ \Rightarrow \lceil\frac{-x_1+1}{b_1}\rceil\leq t\leq\lfloor\frac{y_1-1}{a_1}\rfloor \]

所以 \(x,y\) 都为正的解的个数为:

\[\lfloor\frac{y_1-1}{a_1}\rfloor - \lceil\frac{-x_1+1}{b_1}\rceil + 1 \]

若此公式得出的结果不是正数则说明没有 \(x,y\) 都为正的解

模板

Luogu P5656 二元一次不定方程

#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll exgcd(ll a, ll b, ll & x, ll & y)
{
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--) {
        ll a, b, c;
        scanf("%lld%lld%lld", &a, &b, &c);
        ll d, x, y;
        d = exgcd(a, b, x, y);
        if(c % d != 0) {
            printf("-1\n");
            continue;
        }
        x = x * c / d;
        y = y * c / d;
        ll a1 = a / d, b1 = b / d;
        ll t1 = ceil((double)(-x + 1) / b1);
        ll t2 = floor((double)(y - 1) / a1);
        if(t1 > t2) {
            ll ansx = x + t1 * b1;
            ll ansy = y - t2 * a1;
            printf("%lld %lld\n", ansx, ansy);
        } else {
            int cnt = t2 - t1 + 1;
            ll minx = x + t1 * b1;
            ll maxx = x + t2 * b1;
            ll miny = y - t2 * a1;
            ll maxy = y - t1 * a1;
            printf("%d %lld %lld %lld %lld\n", cnt, minx, miny, maxx, maxy);
        }
    }
    return 0;
}
posted @ 2021-12-26 20:16  f(k(t))  阅读(99)  评论(0编辑  收藏  举报