P4549 【模板】裴蜀定理
裴蜀定理
裴蜀定理该定理阐述了两个非零整数的最大公约数(Greatest Common Divisor, GCD)可以通过这两个数的线性组合来表达。具体地,对于任意两个非零整数a和b,存在整数x和y,使得:ax+by=gcd(a,b),这里的gcd(a,b)表示a和b的最大公约数。
换句话说,a和b的最大公约数能够以a和b的整数倍的线性组合的形式出现。更进一步,这样的x和y不仅存在,而且可以有无限多个解。
裴蜀定理的一个重要应用是在求解不定方程ax + by = cax+by=c时,如果c是a和b最大公约数的倍数,那么该方程就有整数解;否则,没有整数解。
例如,考虑a=12和b=15,我们知道\gcd(12, 15)=3gcd(12,15)=3,则存在x和y使得12x + 15y = 312x+15y=3。实际上,一组可能的解是x=-1x=−1和y=1y=1,因为12(-1) + 15(1) = 312(−1)+15(1)=3。
裴蜀定理的证明通常涉及到欧几里得算法(辗转相除法),这个算法也是计算两个整数最大公约数的有效方法。在计算最大公约数的过程中,实际上也可以找到满足裴蜀等式的x和y的值。这种寻找x和y的方法被称为扩展欧几里得算法(Extended Euclidean Algorithm)。
题目描述
P4549 【模板】裴蜀定理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include<iostream>
using namespace std;
#include<cmath>
typedef long long ll;
ll n, a, s;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
int main() {
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> a;
s = gcd(s, abs(a));
}
cout << s;
return 0;
}
代码思路
-
输入处理:
- 首先读入一个整数
n
,表示序列中整数的个数。 - 接下来读入
n
个整数,存储在变量a
中,并依次用这些值更新gcd
的结果。
- 首先读入一个整数
-
辗转相除法实现:
- 定义了一个
gcd
函数,接收两个long long
类型的参数a
和b
。 - 在
gcd
函数内部,递归调用自身,直到b
变为0,此时返回a
作为两数的最大公约数。 - 这里的递归终止条件
return b == 0 ? a : gcd(b, a % b);
正是辗转相除法的核心,即不断用较小的数去除较大的数,直到余数为0,此时的除数就是最大公约数。
- 定义了一个
-
最大公约数的计算:
- 在
main
函数中,使用变量s
来保存序列中所有整数的最大公约数,初始值未知,但随着循环进行,s
将被更新为当前已读取的所有整数的最大公约数。 - 循环遍历每一个输入的整数
a
,调用gcd
函数更新s
的值,即s = gcd(s, abs(a));
。这里使用abs
函数是为了确保即使输入中有负数,也能正确计算其绝对值的GCD。
- 在
-
输出结果:循环结束后,
s
包含了整个序列的最大公约数,将其输出。
P1516 青蛙的约会(扩展欧几里得方程)
题目描述
P1516 青蛙的约会 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
typedef long long ll;
using namespace std;
int ex_gcd(ll a, ll b, ll& x, ll& y) {
if (!b) {
x = 1, y = 0;
return a;
}
ll nx, ny;
ll ans = ex_gcd(b, a % b, nx, ny);
x = ny;
y = nx - (a / b) * ny;
return ans;
}
ll gcd(ll a, ll b) {
return b ? gcd(b, a % b) : a;
}
signed main() {
ll x, y, n, m, L;
cin >> x >> y >> m >> n >> L;
ll x_, y_;
ll a = m - n;
ll b = L;
ll c = y - x;
if (a < 0) {
a = -a;
c = -c;
}
ll g = ex_gcd(a, b, x_, y_);
if (c % g) {
cout << "Impossible\n";
return 0;
}
x_ = x_ * c / g;
cout << (x_ % (b / g) + (b / g)) % (b / g);
return 0;
}
代码思路
-
扩展欧几里得算法 (
ex_gcd
函数):- 此函数计算两个整数 aa 和 bb 的最大公约数(GCD),同时找到整数 xx 和 yy,使得 ax + by = gcd(a, b)ax+by=gcd(a,b)。
- 当 b = 0b=0 时,直接返回 aa 并设置 x = 1, y = 0x=1,y=0。
- 否则,递归调用
ex_gcd(b, a % b)
并更新 xx 和 yy 的值。 - 返回 gcd(a, b)gcd(a,b)。
-
辗转相除法 (
gcd
函数):一个简化的 GCD 计算函数,使用递归实现辗转相除法。 -
主程序 (
main
函数):- 读取输入:x, y, m, n, Lx,y,m,n,L。
- 设置 a = m - na=m−n 和 b = Lb=L,以及 c = y - xc=y−x。
- 如果 a < 0a<0,则调整 aa 和 cc 的符号,确保 a > 0a>0,这是因为 GCD 不受正负号的影响。
- 调用
ex_gcd
函数计算 aa 和 bb 的 GCD 并找到对应的 x'x′ 和 y'y′。 - 检查 cc 是否能被 gcd(a, b)gcd(a,b) 整除,如果不能,则无解,输出 "Impossible"。
- 如果可整除,计算 x' = x' \cdot c / gcd(a, b)x′=x′⋅c/gcd(a,b),这是满足原方程的一个特解。
- 输出满足条件的最小非负整数解 tt,即 (x' \mod (b / gcd(a, b)))(x′mod(b/gcd(a,b)))。这里使用模运算找到满足条件的最小非负解。
P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪
题目描述
P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
#include <cstring>
#include <algorithm>
typedef long long LL;
// 扩展欧几里得算法
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
LL d = exgcd(b, a % b, x, y);
LL temp = x;
x = y;
y = temp - a / b * y;
return d;
}
// 中国剩余定理
LL CRT(LL m[], LL r[], int n) {
LL M = 1, ans = 0;
for (int i = 0; i < n; ++i) {
M *= m[i];
}
for (int i = 0; i < n; ++i) {
LL c = M / m[i];
LL x, y;
exgcd(c, m[i], x, y);
ans = (ans + r[i] * c * x % M) % M;
}
return (ans % M + M) % M;
}
int main() {
int n;
std::cin >> n;
LL a[11], b[11];
for (int i = 0; i < n; ++i) {
std::cin >> a[i] >> b[i];
}
std::cout << CRT(a, b, n) << std::endl;
return 0;
}
代码思路
exgcd
函数:
- 目的是求解两个数
a
和b
的最大公约数d
,同时求出满足ax + by = d
的x
和y
值。 - 通过递归的方式,先处理
b == 0
的情况,直接设置x = 1
,y = 0
并返回a
作为最大公约数。 - 否则,先计算
b
和a % b
的扩展欧几里得结果,然后通过计算更新x
和y
的值。
CRT
函数:
- 用于求解中国剩余定理问题。
- 首先计算所有模数
m[i]
的乘积M
。 - 然后对于每个方程,计算
c = M / m[i]
,再通过exgcd
求出c
和m[i]
的解x
和y
,计算出当前方程的贡献值r[i] * c * x % M
,累加到ans
中。 - 最后对
ans
进行取模处理,得到最终结果。
main
函数:
- 读取输入的方程个数
n
。 - 读取每个方程的模数
a[i]
和余数b[i]
。 - 调用
CRT
函数计算结果并输出。