题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(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)将问题转化为求所有数的最大公约数。

  2. 算法选择

    • 裴蜀定理:对于两个整数 \(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
  3. 关键步骤

    • 读取输入:整数 \(n\),以及 \(n\) 个整数 \(A_i\)
    • 初始化res = 0\(0\) 与任何数的 GCD 为该数本身)
    • 迭代求 GCD
      • 遍历 \(i\)\(1\)\(n\)
        • 读取 \(a\)
        • res = gcd(res, abs(a)):计算当前结果与 \(|a|\) 的最大公约数
    • 输出答案 res
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot \log A_{\max})\),其中 \(A_{\max}\)\(|A_i|\) 的最大值。每次 GCD 计算为 \(O(\log A_{\max})\),共 \(n\)
    • 空间复杂度:\(O(1)\),仅使用常数额外变量
  5. 裴蜀定理的核心思想

    • 线性组合与 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\)
    • 适用于不定方程整数解存在性判断、线性组合最小正值、密码学中的模运算等问题

【算法标签】

普及 #裴蜀定理

【代码详解】

#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
posted @ 2026-06-23 11:25  团爸讲算法  阅读(10)  评论(0)    收藏  举报