题解:AcWing 877 扩展欧几里得算法

【题目来源】

AcWing:877 扩展欧几里得算法 - AcWing题库

【题目描述】

给定 \(n\) 对正整数 \(a_i,b_i\),对于每对数,求出一组 \(x_i,y_i\),使其满足 \(a_i\times x_i + b_i\times y_i = gcd(a_i,b_i)\)

【输入】

第一行包含整数 \(n\)

接下来 \(n\) 行,每行包含两个整数 \(a_i,b_i\)

【输出】

输出共 \(n\) 行,对于每组 \(a_i,b_i\),求出一组满足条件的 \(x_i,y_i\),每组结果占一行。

本题答案不唯一,输出任意满足条件的 \(x_i,y_i\) 均可。

【输入样例】

2
4 6
8 18

【输出样例】

-1 1
-2 1

【核心思想】

  1. 问题分析:给定 \(n\) 对正整数 \(a_i, b_i\),求整数 \(x_i, y_i\) 使得 \(a_i \cdot x_i + b_i \cdot y_i = \gcd(a_i, b_i)\)。这是一个经典的数论问题,核心在于利用扩展欧几里得算法(Extended Euclidean Algorithm)在求最大公约数的同时,递推出一组满足裴蜀定理的整数解。

  2. 算法选择

    • 裴蜀定理:对于任意整数 \(a, b\),一定存在整数 \(x, y\) 使得 \(ax + by = \gcd(a, b)\)。扩展欧几里得算法就是构造性地求出这样一组解
    • 递归 + 回溯:先递归到边界情况 \(b = 0\),此时 \(x = 1, y = 0\) 显然成立;然后在回溯过程中利用下一层的解 \((x_1, y_1)\) 推导出当前层的解
    • 递推公式:设递归调用 exgcd(b, a % b, x1, y1) 返回了解 \((x_1, y_1)\),则当前层的解为:
      • \(x = y_1\)
      • \(y = x_1 - \lfloor a/b \rfloor \cdot y_1\)
  3. 关键步骤

    • 读取输入:整数 \(n\)(查询次数),以及 \(n\)\((a, b)\)
    • 扩展欧几里得函数 exgcd(a, b, x, y)
      • \(b = 0\)\(x = 1, y = 0\),返回 \(a\)(此时 \(\gcd(a, 0) = a\)
      • 递归调用 d = exgcd(b, a % b, x, y),此时 \(x, y\) 是下一层的解
      • 保存临时值 int x1 = x, y1 = y
      • 更新当前层解:
        • \(x = y_1\)
        • \(y = x_1 - (a / b) \cdot y_1\)
      • 返回 \(d\)(即 \(\gcd(a, b)\)
    • 输出解:对于每组数据,调用 exgcd(a, b, x, y) 后输出 \(x, y\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot \log(\min(a_i, b_i)))\)。单次 exgcd 的时间复杂度与欧几里得算法相同,为 \(O(\log(\min(a, b)))\)
    • 空间复杂度:\(O(\log(\min(a_i, b_i)))\),递归栈深度
  5. 扩展欧几里得算法的核心思想

    • 构造性证明裴蜀定理:不仅证明了解的存在性,还给出了一种系统性的构造方法,这是其相对于普通欧几里得算法的核心扩展
    • 递归递推关系:由 \(\gcd(a, b) = \gcd(b, a \bmod b)\),若已知 \(bx_1 + (a \bmod b)y_1 = \gcd(b, a \bmod b)\),则可推出:
      • \(bx_1 + (a - \lfloor a/b \rfloor \cdot b)y_1 = \gcd(a, b)\)
      • \(ay_1 + b(x_1 - \lfloor a/b \rfloor \cdot y_1) = \gcd(a, b)\)
      • 因此 \(x = y_1,\; y = x_1 - \lfloor a/b \rfloor \cdot y_1\)
    • 通解公式:若 \((x_0, y_0)\) 是一组特解,则所有解可表示为:
      • \(x = x_0 + k \cdot \frac{b}{\gcd(a, b)}\)
      • \(y = y_0 - k \cdot \frac{a}{\gcd(a, b)}\),其中 \(k \in \mathbb{Z}\)
    • 求逆元的通用方法:扩展欧几里得算法不要求模数为质数,只要 \(\gcd(a, p) = 1\) 即可求出 \(a\)\(p\) 的逆元(解 \(ax + py = 1\) 中的 \(x\)),适用范围比费马小定理更广
    • 适用于不定方程整数解、模逆元(任意模数)、线性同余方程、中国剩余定理等问题

【算法标签】

扩展欧几里得算法

【代码详解】

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

// 扩展欧几里得算法,用于求解 ax + by = gcd(a, b) 的一组整数解
int exgcd(int a, int b, int &x, int &y)
{
    if (b == 0) { // 如果 b 为 0,递归终止
        x = 1, y = 0; // 解为 x = 1, y = 0
        return a; // 返回 gcd(a, b)
    }
    int r = exgcd(b, a % b, x, y); // 递归求解
    int x1 = x, y1 = y; // 保存递归结果
    // 更新 x 和 y 的值
    x = y1;
    y = x1 - (a / b) * y1;
    return r; // 返回 gcd(a, b)
}

int main()
{
    int n; // 定义整数 n,表示查询的次数
    cin >> n; // 输入查询的次数
    while (n--) { // 遍历每个查询
        int a, b; // 定义整数 a 和 b
        cin >> a >> b; // 输入 a 和 b
        int x, y; // 定义变量 x 和 y,用于存储扩展欧几里得算法的解
        exgcd(a, b, x, y); // 调用扩展欧几里得算法求解
        cout << x << " " << y << endl; // 输出解 x 和 y
    }
    return 0; // 程序结束
}
#include <bits/stdc++.h>
using namespace std;

// 扩展欧几里得算法
// 求解 ax + by = gcd(a, b) 的一组整数解 (x, y)
// 返回 a 和 b 的最大公约数 gcd(a, b)
int exgcd(int a, int b, int &x, int &y)
{
    // 基本情况:b = 0
    if (!b)
    {
        x = 1;   // 当 b=0 时,ax = gcd(a,0) = a
        y = 0;   // 所以 x=1, y=0
        return a; // 返回最大公约数
    }

    // 递归调用,交换 x 和 y 的位置
    // 因为我们要从下一层的结果推导当前层的解
    int d = exgcd(b, a % b, y, x);

    // 根据递推关系更新 y
    // 公式:y = y - (a/b) * x
    y -= a / b * x;

    return d;  // 返回最大公约数
}

int main()
{
    int n;  // 查询次数
    cin >> n;

    while (n--)  // 处理每个查询
    {
        int a, b, x, y;
        cin >> a >> b;  // 输入 a 和 b

        // 调用扩展欧几里得算法
        int d = exgcd(a, b, x, y);

        // 输出方程 ax + by = gcd(a, b) 的一组解
        cout << x << " " << y << endl;

        // 验证:ax + by 应该等于 d
        // cout << "验证: " << a << "*" << x << " + " << b << "*" << y << " = " << a*x + b*y << " (gcd = " << d << ")" << endl;
    }

    return 0;
}

【运行结果】

2
4 6
-1 1
8 18
-2 1
posted @ 2026-06-23 11:24  团爸讲算法  阅读(10)  评论(0)    收藏  举报