[题解]P5656 【模板】二元一次不定方程 (exgcd)
若存在\(ax+by=c\),则可以根据特解\(x,y\)求出任意通解\(x',y'\):
\(\begin{cases}
x'=x+k*\frac{b}{\gcd(a,b)}\\
y'=y-k*\frac{a}{\gcd(a,b)}
\end{cases}(k\in \mathbb{Z})\)
求特解的方法是「扩展欧几里得(exgcd)」,如果没接触过可以先阅读此文或搜索更多信息。
该方程有整数解当且仅当\(\gcd(a,b)|c\)。所以不满足直接输出-1
。
接下来考虑怎么求通解。
首先,exgcd求出来的结果是\(ax+by=\gcd(a,b)\),我们要满足\(ax+by=c\),必须先把\(x,y\)都乘\(\frac{c}{\gcd(a,b)}\)。
然后,我们知道\(k\)有一个取值范围,这个范围内每个\(k\)都对应一组解。我们只需要算出\(x',y'>0\)时\(k\)的取值范围,一切就好弄了。
记\(\frac{b}{\gcd(a,b)}=r,\frac{a}{\gcd(a,b)}=s\)。则:
- \(x'>0\iff x+kr>0\iff k>-\frac{x}{r}\),故\(k\)的最小值\(mink=\lfloor -\frac{x}{r}\rfloor+1\)。
- \(y'>0\iff y-ks>0\iff k<\frac{y}{s}\),故\(k\)的最大值\(maxk=\lceil \frac{y}{s}\rceil-1\)。
如果\(mink>maxk\),说明有整数解但没有正整数解,输出\(2\)个值:
- \(x\)最小值:\(x+mink*r\)。
- \(y\)最小值:\(y-maxk*s\)。
否则说明有整数解,需要输出\(5\)个值:
- 个数:\(maxk-mink+1\)。
- \(x\)最小值:\(x+mink*r\)。
- \(y\)最小值:\(y-maxk*s\)。
- \(x\)最大值:\(x+maxk*r\)。
- \(y\)最大值:\(y-mink*s\)。
注意:开long long
。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,a,b,c;
int exgcd(int a,int b,int& x,int& y){
int d;
if(b==0) x=1,y=0,d=a;
else d=exgcd(b,a%b,y,x),y-=a/b*x;
return d;
}
signed main(){
cin>>t;
int x,y;
while(t--){
cin>>a>>b>>c;
int gcd=exgcd(a,b,x,y);
if(c%gcd!=0){
cout<<"-1\n";
continue;
}
int stepx=b/gcd,stepy=a/gcd;
x*=c/gcd,y*=c/gcd;
int mink=floor(-1.0*x/stepx)+1,maxk=ceil(1.0*y/stepy)-1;
if(mink>maxk){
cout<<x+mink*stepx<<" "<<y-maxk*stepy<<"\n";
}else{
cout<<maxk-mink+1<<" ";
cout<<x+mink*stepx<<" "<<y-maxk*stepy<<" ";
cout<<x+maxk*stepx<<" "<<y-mink*stepy<<"\n";
}
}
return 0;
}