GCD 与 LCM
💡 核心思想
GCD(最大公约数)和 LCM(最小公倍数)是数论基础。辗转相除法(欧几里得算法)$O(\log \min(a,b))$ 求 GCD。扩展欧几里得算法可以求 $ax + by = \gcd(a,b)$ 的整数解。
GCD 和 LCM 看起来简单,但应用非常广泛。竞赛中需要掌握三个层次:一是 $\gcd(a,b) = \gcd(b, a \mod b)$ 的递归实现;二是 $\text{lcm}(a,b) = a / \gcd(a,b) \times b$(先除后乘防溢出);三是扩展欧几里得求线性方程的解。
🎯 直觉理解
GCD 就像找两个人共同拥有的"最大度量单位"。比如一段 12cm 和一段 18cm 的木条,你用 6cm 的尺子能量完它们(6 是 GCD)。LCM 则是"最小公共刻度"——12 和 18 的最小公倍数是 36,即 12×3 = 18×2 = 36。
📝 算法流程
- 辗转相除:gcd(a,b) = gcd(b, a%b),gcd(a,0) = a
- LCM = a / gcd(a,b) * b
- 扩展欧几里得:求 ax + by = gcd(a,b) 的解
- 裴蜀定理:ax + by = c 有解当且仅当 gcd(a,b) | c
$$\gcd(a,b) = \gcd(b, a \bmod b)$$
$$\text{lcm}(a,b) = \frac{a \cdot b}{\gcd(a,b)}$$
📊 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间 | $O(\log \min(a,b))$ |
| 空间 | $O(\log \min(a,b))$(递归深度) |
💻 参考实现(C++)
C++ (C++17)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; }
ll lcm(ll a, ll b) { return a / gcd(a,b) * b; }
// 扩展欧几里得
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (!b) { x = 1; y = 0; return a; }
ll d = exgcd(b, a%b, y, x);
y -= a/b * x;
return d;
}
int main() {
ll a, b; cin >> a >> b;
cout << gcd(a,b) << endl;
ll x, y; exgcd(a, b, x, y);
cout << x << " " << y << endl;
return 0;
}⚠️ 常见坑点
LCM 先除后乘防止溢出
扩展欧几里得的 x, y 可能是负数
gcd(0, 0) 未定义,需要特判
负数取模的行为在不同语言中不同
📚 相关题目
| 题目 | 来源 | 难度 | 备注 |
|---|---|---|---|
| P1029 最大公约数与最小公倍数 | 洛谷 | CSP-J | GCD/LCM |
| P1082 求逆元 | 洛谷 | CSP-S | 扩展欧几里得 |