传送门:P4549 【模板】裴蜀定理

更佳的阅读体验:洛谷 P4549 题解


算法介绍

给定一个长度为 $n$ 的整数序列 $A$ 和一个待定整数序列 $X$,求 $S = \sum \limits_{i = 1}^{n} A_i \times X_i$ 在正数范围内的最小值。

看上去似乎有点无从下手。

那先从简单的开始!我们来考虑 $n = 2$ 的情况,此时问题简化为求 $A_1 X_1 + A_2 X_2$ 的最小值。

如果我们设 $a, b$ 是不全为 $0$ 的整数,那么有裴蜀定理(Bézout's Lemma):

  • 对于任意整数 $x, y$,有 $\gcd (a, b) \mid (ax + by)$。
  • 存在整数 $x, y$,使得 $ax + by = \gcd (a, b)$。

因此,$ax + by$ 的最小正整数值显然是 $\gcd (a, b)$。上述问题的答案就是 $\gcd(A_1, A_2)$。

那么,当 $n \ge 3$ 呢?我们尝试推广裴蜀定理。

假设有三个整数 $a, b, c$。我们首先对前两个数应用裴蜀定理,存在整数 $x_1, x_2$,使得 $ax_1 + bx_2 = \gcd (a, b)$。接下来,将这个结果与第三个数 $c$ 结合,即存在整数 $k, x_3$,使得 $\gcd (a, b) \times k + cx_3 = \gcd( \gcd(a, b), c)$。

我们知道,最大公约数有性质:$\gcd (a, b, c) = \gcd (\gcd(a, b), c)$,因此存在整数组合使得 $ax_1 + bx_2 + cx_3 = \gcd(a, b, c)$,且不存在更小的正数解。

这样推广下去,最终无论 $n$ 的大小,问题的最小正数解都是所有 $A_i$ 的最大公约数的绝对值。因此,我们只需要计算整个序列 $A$ 的最大公约数,并取其绝对值即可。

正确性证明

我们设 $a, b$ 是不全为 $0$ 的整数,那么一定有 $\gcd(a, b) \mid a$ 且 $\gcd(a, b) \mid b$。设 $x, y$ 为整数,则有 $\gcd(a, b) \mid ax$ 且 $\gcd(a, b) \mid bx$,因此 $\gcd(a, b) \mid (ax + by)$。

对于定理的第二点,若 $a$ 和 $b$ 任意一个等于 $0$,则 $\gcd(a, b)$ 的值为非零数的值,此时定理显然成立。

若 $ab \neq 0$,由于 $\gcd(a, b) = \gcd(a, -b)$,我们不妨设 $a, b$ 都大于 $0$,$a \ge b$,且有 $\gcd(a, b) = d$。

对 $ax + by = d$,两边同时除以 $d$,得到 $a_1x + b_1y = 1$,其中 $a_1, b_1$ 互质。接下来证明 $a_1x + b_1y = 1$。

我们先来回顾一下辗转相除法:$\gcd(a, b) \rightarrow \gcd(b, a \bmod b) \rightarrow \cdots$。我们把模出来的数记作 $r$,则有:

$$ \gcd(a_1, b_1) = \gcd(b_1, r_1) = \gcd(r_1, r_2) = \cdots = \gcd(r_{n - 1}, r_n) = 1 $$

把辗转相除法中的运算展开成带余除法,得到:

$$ \begin{align*} a_1 & = q_1b_1 + r_1 & (0 \le r_1 < b_1) \\ b_1 & = q_2r_1 + r_2 & (0 \le r_2 < r_1) \\ r_1 & = q_3r_2 + r_3 & (0 \le r_3 < r_2) \\ & \cdots & \\ r_{n - 3} & = q_{n - 1}r_{n - 2} + r_{n - 1} & \\ r_{n - 2} & = q_nr_{n - 1} + r_n & \\ r_{n - 1} & = q_{n + 1}r_n \end{align*} $$

我们令辗转相除法在除到互质的时候退出,则 $r_n = 1$,所以有:

$$ r_{n - 2} = q_nr_{n - 1} + 1 $$

即:

$$ 1 = r_{n - 2} - q_nr_{n - 1} $$

用倒数第三个式子 $r_{n - 1} = r_{n - 3} - q_{n - 1}r_{n - 2}$ 代入上式,得:

$$ 1 = (1 + q_nq_{n - 1}) r_{n - 2} - q_nr_{n - 3} $$

然后用同样的办法逐个地消去 $r_{n - 2}, \cdots, r_1$,即可证得 $1 = a_1x + b_1y$。

代码实现

#include <iostream>
#include <algorithm>
using namespace std;

int n, a, ans;

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cin >> n >> a;
    ans = a;
    for (int i = 2; i <= n; ++i)
        cin >> a, ans = __gcd(ans, abs(a));
    cout << ans << '\n';
    return 0;
}
最后修改:2025 年 05 月 14 日
如果觉得我的文章对你有用,请随意赞赏