【算法模板】数论:裴蜀定理

概念

裴蜀定理(Bézout’s Identity)是数论中的一个重要定理,涉及整数的线性组合。定理陈述如下:

对于任何整数 a 和 b,如果 d 是 a 和 b 的最大公约数,那么存在整数 x 和 y 使得: ax+by=d

换句话说,最大公约数 d 可以表示为 a 和 bbb 的线性组合。

欧几里得算法证明

欧几里得算法是一种用于计算两个整数的最大公约数的算法。我们可以利用这个算法来证明裴蜀定理。

  1. 算法步骤:
    • 如果 b=0 ,那么 a 就是 a 和 b 的最大公约数。此时 a=d ,且 a⋅1+b⋅0=a。
    • 否则,令 r 是 a 除以 b 的余数,即 a=bq+r ,其中 q 是商。然后用欧几里得算法递归地计算 b 和 r 的最大公约数。
  2. 证明过程:
    • 首先,我们知道 d=gcd⁡(a,b) 也是 d=gcd⁡(b,r) ,因为 r=a−bq 。根据递归过程,我们可以不断减小问题的规模,最终在某一步骤中,我们会得到 r=0 ,此时 b 是最大公约数。
    • 在每一步,我们可以记录线性组合的形式。具体来说: r=a−bq 如果我们知道 b 和 r 的最大公约数可以写成: b⋅x1+r⋅y1=d 那么我们可以用 r=a−bq 替换 r: b⋅x1+(a−bq)⋅y1=d 展开并整理得到: a⋅y1+b⋅(x1−qy1)=d 因此,我们可以看到 d 依然是 a 和 b 的线性组合。
  3. 详细的证明递归部分:
    • 初始情况:如果 b=0 ,则最大公约数 d=a ,且 a⋅1+b⋅0=a ,此时 x=1 和 y=0 。
    • 递归情况:使用欧几里得算法得到 a=bq+r ,则 gcd⁡(a,b)=gcd⁡(b,r) ,我们可以继续应用算法到 b 和 r 。通过递归地应用线性组合,我们最终可以写出最大公约数的形式。

通过这个过程,我们证明了裴蜀定理,即对于任何两个整数 a 和 b,它们的最大公约数 d 总可以表示成它们的某个线性组合形式 ax+by=d 。

推广

对于任意整数 a1,a2,…,an ,如果 d 是这些整数的最大公约数,那么存在整数 x1,x2,…,xn 使得: a1x1+a2x2+⋯+anxn=d

换句话说,多个整数的最大公约数 d 可以表示为这些整数的线性组合。

模板

inline int gcd(int x, int y) { return y ? gcd(y, x % y) : !x; }
int n;
int main() {
    scanf("%d", &n);
    int ans = 0, tmp;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &tmp);
        if (tmp < 0) tmp = -tmp;
        ans = gcd(ans, tmp);
    }
    printf("%d", ans);
}

模板题

P4549 【模板】裴蜀定理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

给定一个包含 n 个元素的整数序列 A,记作 A1, A2, A3, …, An。

我们的目标是求另一个包含 n 个元素的待定整数序列 X,其中 S = ∑(Ai × Xi)(i 从 1 到 n)。要求 S > 0 且 S 尽可能的小。

#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;cin>>n;
    vector<int> v(n);
    for(int &i:v)cin>>i;
    int g=v.front();
    for(int i:v)g=gcd(g,i);
    cout<<g<<endl;
    return 0;
}

B-Crash Test_2024牛客暑期多校训练营3 (nowcoder.com)

我们考虑以下碰撞测试的简化版本。最初,汽车的前部朝向墙壁,距离墙壁 D 米。该碰撞测试提供了 n 种助推器,其中第 i 种助推器的推力性能为 h,并且每种类型的助推器都有充足的数量。假设汽车前部和墙壁之间的当前距离为 d,我们使用推力性能为 h 的助推器。当 d≥h 时,汽车将向前移动 h 米,然后停止。否则,汽车将向前移动 d 米,撞到墙上,然后反弹 h−d 米,然后停下来,仍然面向墙壁。

现在,您想知道,通过任意数量的操作(包括无操作),汽车前部和墙壁之间的最小距离是多少?

#include <bits/stdc++.h>
using namespace std;
#define int long long

signed main() {
    int n,m;
    cin>>n>>m;
    vector<int> v(n);
    for(int &i:v)cin>>i;
    int g=v.front();
    for(int i:v)g=gcd(g,i);
    int x=m/g;
    cout<<min(m-x*g,(x+1)*g-m);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值