题解: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
【核心思想】
-
问题分析:给定 \(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)在求最大公约数的同时,递推出一组满足裴蜀定理的整数解。
-
算法选择:
- 裴蜀定理:对于任意整数 \(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\)
-
关键步骤:
- 读取输入:整数 \(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\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot \log(\min(a_i, b_i)))\)。单次 exgcd 的时间复杂度与欧几里得算法相同,为 \(O(\log(\min(a, b)))\)
- 空间复杂度:\(O(\log(\min(a_i, b_i)))\),递归栈深度
-
扩展欧几里得算法的核心思想:
- 构造性证明裴蜀定理:不仅证明了解的存在性,还给出了一种系统性的构造方法,这是其相对于普通欧几里得算法的核心扩展
- 递归递推关系:由 \(\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
浙公网安备 33010602011771号