题解:洛谷 P4549 裴蜀定理
【题目来源】
洛谷:P4549 【模板】裴蜀定理 - 洛谷(luogu.com.cn)
【题目描述】
给定一个包含 \(n\) 个元素的整数序列 \(A\),记作 \(A_1,A_2,A_3,...,A_n\)。
求另一个包含 \(n\) 个元素的待定整数序列 \(X\),记 \(S=\sum\limits_{i=1}^{n}A_i×X_i\),使得 \(S>0\) 且 \(S\) 尽可能的小。
【输入】
第一行一个整数 \(n\),表示序列元素个数。
第二行 \(n\) 个整数,表示序列 \(A\)。
【输出】
一行一个整数,表示 \(S>0\) 的前提下 S 的最小值。
【输入样例】
2
4059 -1782
【输出样例】
99
【核心思想】
-
问题分析:给定 \(n\) 个整数 \(A_1, A_2, \ldots, A_n\),求整数序列 \(X_1, X_2, \ldots, X_n\) 使得 \(S = \sum_{i=1}^{n} A_i \cdot X_i > 0\) 且 \(S\) 尽可能小。这是一个经典的数论问题,核心在于利用裴蜀定理(Bezout's Identity)将问题转化为求所有数的最大公约数。
-
算法选择:
- 裴蜀定理:对于两个整数 \(a, b\),存在整数 \(x, y\) 使得 \(ax + by = \gcd(a, b)\)。推广到 \(n\) 个数:存在整数 \(x_1, x_2, \ldots, x_n\) 使得 \(\sum_{i=1}^{n} a_i x_i = \gcd(a_1, a_2, \ldots, a_n)\)
- 欧几里得算法:通过辗转相除法求所有数绝对值的最大公约数
- 绝对值处理:由于 \(X_i\) 可以为负数,\(A_i\) 的符号不影响结果,故对每个数取绝对值后求 GCD
-
关键步骤:
- 读取输入:整数 \(n\),以及 \(n\) 个整数 \(A_i\)
- 初始化:
res = 0(\(0\) 与任何数的 GCD 为该数本身) - 迭代求 GCD:
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 读取 \(a\)
res = gcd(res, abs(a)):计算当前结果与 \(|a|\) 的最大公约数
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 输出答案
res
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot \log A_{\max})\),其中 \(A_{\max}\) 为 \(|A_i|\) 的最大值。每次 GCD 计算为 \(O(\log A_{\max})\),共 \(n\) 次
- 空间复杂度:\(O(1)\),仅使用常数额外变量
-
裴蜀定理的核心思想:
- 线性组合与 GCD 的关系:所有整数线性组合 \(\sum a_i x_i\)(\(x_i \in \mathbb{Z}\))构成的集合,恰好是 \(\gcd(a_1, a_2, \ldots, a_n)\) 的所有整数倍的集合
- 证明思路:设 \(d = \gcd(a_1, \ldots, a_n)\),则 \(d\) 整除任何线性组合;反过来,通过扩展欧几里得算法可以构造出等于 \(d\) 的线性组合
- 最小正线性组合:由于所有线性组合都是 \(d\) 的倍数,最小的正线性组合就是 \(d\) 本身
- 符号无关性:因为 \(X_i\) 可以取任意整数(包括负数),\(A_i\) 的符号不影响可表示的数值集合,故只需对 \(|A_i|\) 求 GCD
- 与扩展欧几里得的联系:裴蜀定理保证了存在性,扩展欧几里得算法可以具体构造出满足条件的 \(X_i\)(本题只需输出最小值,无需输出具体 \(X_i\))
- 适用于不定方程整数解存在性判断、线性组合最小正值、密码学中的模运算等问题
- 线性组合与 GCD 的关系:所有整数线性组合 \(\sum a_i x_i\)(\(x_i \in \mathbb{Z}\))构成的集合,恰好是 \(\gcd(a_1, a_2, \ldots, a_n)\) 的所有整数倍的集合
【算法标签】
普及 #裴蜀定理
【代码详解】
#include<iostream>
#include<cmath>
using namespace std;
int n, a, s; // n: 数字数量,a: 当前输入的数字,s: 当前的最大公约数
// 计算最大公约数(辗转相除法)
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int main()
{
cin >> n; // 读入数字数量
for (int i = 1; i <= n; i++)
{
cin >> a; // 读入当前数字
s = gcd(s, abs(a)); // 计算当前数字绝对值与前一个最大公约数的最大公约数
}
cout << s; // 输出所有数字绝对值的最大公约数
return 0;
}
// 使用acwing模板二刷
#include <bits/stdc++.h>
using namespace std;
int n, a; // n: 数字数量,a: 临时变量
// 计算最大公约数(辗转相除法)
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a; // 如果b不为0则递归,否则返回a
}
int main()
{
cin >> n; // 读入数字数量
int res = 0; // 初始最大公约数为0
for (int i = 1; i <= n; i++)
{
int a;
cin >> a; // 读入当前数字
res = gcd(res, abs(a)); // 计算当前数字绝对值与之前结果的最大公约数
}
cout << res << endl; // 输出所有数字绝对值的最大公约数
return 0;
}
【运行结果】
2
4059 -1782
99
浙公网安备 33010602011771号