题解:P5656 【模板】二元一次不定方程 (exgcd)
lailai0916 · · 题解
参考资料
- 最大公约数 - OI Wiki
- Euclidean algorithm - Wikipedia
- Extended Euclidean algorithm - Wikipedia
题意简述
给定
- 若无整数解,输出
-1; - 若有正整数解,输出正整数解中,解的数量、
x 的最小值、y 的最小值、x 的最大值、y 的最大值; - 若无正整数解,输出
x 的最小正数值和y 的最小正数值。
基础知识
一次不定方程
一次不定方程(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)可以求
设一组整数解为
根据欧几里得算法:
所以:
又因为:
所以:
所以:
将
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};
}
解题思路
我们先用 扩展欧几里得算法 求出最大公约数
根据裴蜀定理,当 -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;
}