This commit is contained in:
Zengtudor 2025-02-05 13:13:39 +08:00
parent 73d1505579
commit 5b9155aabe
3 changed files with 178 additions and 1 deletions

Binary file not shown.

27
src/2/P4549.cpp Normal file
View File

@ -0,0 +1,27 @@
#include <cstdlib>
#include <iostream>
int gcd(int a,int b){
if(b==0)return a;
return gcd(b,a%b);
}
template<class T>
void abss(T &t){
t=abs(t);
}
int main(){
int n;
std::cin>>n;
int tmpa,tmpb;
std::cin>>tmpa;
abss(tmpa);
for(int i{1};i<n;i++){
std::cin>>tmpb;
abss(tmpb);
tmpa = gcd(tmpa,tmpb);
}
std::cout<<tmpa<<"\n";
}

View File

@ -34,4 +34,154 @@ GCD CRT 逆元
3. 回溯到 $\gcd(30, 12)$,计算 $x = 1$, $y = -2$,满足 $30 \cdot 1 + 12 \cdot (-2) = 6$。
### 总结
代码通过递归分解问题,逐步缩小规模。每次递归后,利用子问题的解 $x'$ 和 $y'$ 推导当前层的解 $x$ 和 $y$,最终得到满足 $ax + by = \gcd(a, b)$ 的整数解。
代码通过递归分解问题,逐步缩小规模。每次递归后,利用子问题的解 $x'$ 和 $y'$ 推导当前层的解 $x$ 和 $y$,最终得到满足 $ax + by = \gcd(a, b)$ 的整数解。
好的,下面是对这段 C++ 代码的逐行详细解释:
```cpp
int Exgcd(int a, int b, int &x, int &y) {
```
- 定义了一个名为 `Exgcd` 的函数。
- 函数参数包括两个整数 `a``b`,以及两个引用参数 `x``y`
- `x``y` 用来输出结果,表示满足方程 $ ax + by = \text{GCD}(a, b) $ 的系数。
```cpp
if (!b) {
```
- 检查 `b` 是否为 0。`!b` 表示 `b` 为 0 的时候,条件为真。
```cpp
x = 1;
y = 0;
return a;
```
- 如果 `b` 是 0则最大公约数是 `a`,同时根据贝祖定理,我们可以得到系数:
- 设置 `x = 1`(表示 $a \cdot 1$
- 设置 `y = 0`(表示 $b \cdot 0$)。
- 返回 `a`,也就是最大公约数。
```cpp
}
```
- 结束了对 `b` 是否为 0 的检查。
```cpp
int d = Exgcd(b, a % b, x, y);
```
- 递归调用 `Exgcd` 函数,参数为 `b``a % b`(即 `a` 除以 `b` 的余数)。
- 这个递归调用会继续进行,直到 `b` 为 0返回的最大公约数会被存储在 `d` 中,同时计算得到的 $x$ 和 $y$ 值也会在递归返回的过程中更新。
```cpp
int t = x;
```
- 创建一个临时变量 `t`,存储 `x` 的当前值。这个值在接下来的代码中会被更新。
```cpp
x = y;
```
- 将 `y` 的值赋给 `x`。在执行 `Exgcd(b, a % b)` 时,新的 $x$ 是旧的 $y$。
```cpp
y = t - (a / b) * y;
```
- 更新 `y` 的值。根据公式:
- 我们把旧的 `y` 乘以 $ -\left(\frac{a}{b}\right) $(即取整运算向下取整),再减去 `t`(旧的 `x` 值)。
- 这个步骤是用于反向计算贝祖系数。
```cpp
return d;
```
- 返回最大公约数 `d`
整个函数的目的是找到两个数 $a$ 和 $b$ 的最大公约数,同时计算出系数 $x$ 和 $y$,使得 $ax + by = \text{GCD}(a, b)$ 成立。这个算法在许多数字和代数问题中都非常有用,尤其是在计算模反元素和求解线性同余方程时。
好的!以下是逐行注释的 **扩展欧几里得算法Exgcd** 的 C++ 代码解释,帮助理解递归过程和解的推导逻辑:
```cpp
int Exgcd(int a, int b, int &x, int &y) {
// 递归终止条件:当 b = 0 时gcd(a, 0) = a
if (!b) {
// 此时方程 ax + 0*y = a显然 x = 1, y 可以是任意整数(此处设为 0
x = 1; // 基础解 x = 1
y = 0; // 基础解 y = 0为了后续递归回溯时计算方便
return a; // 返回最大公约数
}
// 递归调用:计算 gcd(b, a % b),并得到对应的解 x' 和 y'
// 注意:这里的 x 和 y 会被递归过程不断修改,最终得到的是下一层的解
int d = Exgcd(b, a % b, x, y);
// 保存递归返回后的 x此时 x 已经是下一层的解 x'
int t = x;
// 当前层的 x 等于下一层的 y'
// 根据公式推导x = y'
x = y;
// 当前层的 y 等于下一层的 x' - (a / b) * y'
// 公式推导y = x' - floor(a / b) * y'
y = t - (a / b) * y;
// 返回最大公约数(递归过程中一直传递 d最终返回给最外层
return d;
}
```
---
### **关键步骤图解**
假设输入 `a = 30`, `b = 12`,递归过程如下:
1. **第一层递归** `Exgcd(30, 12, x, y)`:
- 计算 `a % b = 30 % 12 = 6`
- 进入第二层递归 `Exgcd(12, 6, x, y)`
2. **第二层递归** `Exgcd(12, 6, x, y)`:
- 计算 `a % b = 12 % 6 = 0`
- 进入第三层递归 `Exgcd(6, 0, x, y)`
3. **第三层递归** `Exgcd(6, 0, x, y)`:
- 终止条件触发:`x = 1`, `y = 0`, 返回 `d = 6`
4. **回溯到第二层** `Exgcd(12, 6, x, y)`:
- `t = x = 1`(保存下一层的 x' = 1
- `x = y = 0`(当前层 x = y' = 0
- `y = t - (12/6)*y = 1 - 2*0 = 1`(当前层 y = 1
- 返回 `d = 6`
5. **回溯到第一层** `Exgcd(30, 12, x, y)`:
- `t = x = 0`(保存下一层的 x' = 0
- `x = y = 1`(当前层 x = y' = 1
- `y = t - (30/12)*y = 0 - 2*1 = -2`(当前层 y = -2
- 返回 `d = 6`
最终得到解:`x = 1`, `y = -2`,满足 `30*1 + 12*(-2) = 6`(即 gcd(30, 12) = 6
---
### **核心公式推导**
递归过程基于以下恒等式:
$$
\gcd(a, b) = \gcd(b, a \% b)
$$
假设递归后得到的解为 $ x' $ 和 $ y' $,满足:
$$
b \cdot x' + (a \% b) \cdot y' = \gcd(b, a \% b)
$$
利用 $ a \% b = a - \lfloor a/b \rfloor \cdot b $,可重写为:
$$
a \cdot y' + b \cdot \left( x' - \lfloor a/b \rfloor \cdot y' \right) = \gcd(a, b)
$$
因此,当前层的解为:
$$
x = y', \quad y = x' - \lfloor a/b \rfloor \cdot y'
$$
---
### **总结**
- **递归逻辑**:通过不断缩小问题规模(`a → b`, `b → a%b`),直到 `b = 0` 时终止。
- **解的回溯**:利用递归返回的解 `x'``y'`,推导当前层的解 `x``y`
- **变量交换**:通过临时变量 `t` 保存中间值,确保计算顺序正确。
这个算法高效地求解了 **贝祖等式Bézout's identity** 的整数解,同时计算最大公约数。