最大公约数
参考资料
欧几里得算法
欧几里得算法(辗转相除法)可以求两个整数的 最大公约数(Greatest Common Divisor,GCD)。
- 递归
- 迭代
ll Gcd(ll a,ll b)
{
return !b?a:Gcd(b,a%b);
}
ll Gcd(ll a,ll b)
{
while(b)
{
ll t=a;
a=b;
b=t%b;
}
return a;
}
扩展欧几里得算法
扩展欧几里得算法(Extended Euclidean algorithm,EXGCD)可以求 的一组整数解。
- 元组
- 引用
tuple<ll,ll,ll> exgcd(ll a,ll b)
{
if(!b)return {a,1,0};
auto [g,x,y]=exgcd(b,a%b);
return {g,y,x-a/b*y};
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0){x=1;y=0;return a;}
ll g=exgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
例题
洛谷 P5656 【模板】二元一次不定方程 (exgcd)
给定不定方程 。()
- 若无整数解,输出
-1。 - 若有正整数解,输出正整数解的数量,在正整数解中 的最小值, 的最小值, 的最大值, 的最大值。
- 若没有正整数解,输出 的最小正整数值, 的最小正整数值。
题解
参考资料
题意简述
给定 ,求解不定方程:
- 若无整数解,输出
-1; - 若有正整数解,输出正整数解的数量、 的最小值、 的最小值、 的最大值、 的最大值;
- 若无正整数解,输出 的最小正数值和 的最小正数值。
基础知识
一次不定方程
一次不定方程(Linear Diophantine Equation)是形如以下形式的方程:
其中 和 都是整数,我们会研究 的整数解。
特别地,当 时为二元一次不定方程:
我们可以调整一下变量名:
裴蜀定理
裴蜀定理(Bézout's Lemma)给出了二元一次不定方程 有整数解 的充要条件。
对于整数 (不全为 ),存在整数 使得 。
换句话说, 有整数解当且仅当 。
证明详见:Bézout's identity - Wikipedia
欧几里得算法
欧几里得算法(辗转相除法)可以求两个整数的 最大公约数(Greatest Common Divisor,GCD)。
证明:设 。若 ,则 ;若 ,则 。故 与 的公约数集合相同,因此 。
显然 ,因此可以递归求解。特别地,。
ll Gcd(ll a,ll b)
{
return !b?a:Gcd(b,a%b);
}
扩展欧几里得算法
扩展欧几里得算法(Extended Euclidean algorithm,EXGCD)可以求 的 一组整数解。
设一组整数解为 :
根据欧几里得算法:
所以:
又因为:
所以:
所以:
将 不断代入递归求解直至 为 递归 回去求解。
解题思路
我们先用 扩展欧几里得算法 求出最大公约数 和一组整数解 。
根据裴蜀定理,当 不是 的倍数时无解,输出 -1。
将方程两边同时乘以 :
此时我们令 ,就得到了原问题的一组解 。
每两组解的 和 分别相差了:
而所有正整数解分别为:
显然 ,可以通过取模得到:
当 最小时 最大, 最小时 最大,因此:
因此正整数的数量为:
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
tuple<ll,ll,ll> exgcd(ll a,ll b)
{
if(!b)return {a,1,0};
auto [g,x,y]=exgcd(b,a%b);
return {g,y,x-a/b*y};
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
while(T--)
{
ll a,b,c;
cin>>a>>b>>c;
auto [g,x,y]=exgcd(a,b);
if(c%g){cout<<-1<<'\n';continue;}
x*=c/g;y*=c/g;
ll dx=b/g,dy=a/g;
ll x_min=(x%dx+dx-1)%dx+1,y_min=(y%dy+dy-1)%dy+1,x_max=(c-b*y_min)/a,y_max=(c-a*x_min)/b;
if(y_max>0)cout<<(x_max-x_min)/dx+1<<' '<<x_min<<' '<<y_min<<' '<<x_max<<' '<<y_max<<'\n';
else cout<<x_min<<' '<<y_min<<'\n';
}
return 0;
}
洛谷 P1082 [NOIP 2012 提高组] 同余方程
求同余方程 的最小正整数解。()
代码(1)
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
tuple<ll,ll,ll> exgcd(ll a,ll b)
{
if(!b)return {a,1,0};
auto [g,x,y]=exgcd(b,a%b);
return {g,y,x-a/b*y};
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll a,b;
cin>>a>>b;
auto [g,x,y]=exgcd(a,b);
cout<<(x%b+b)%b<<'\n';
return 0;
}