给一个二元一次不定方程
ax+by=c
- 无整数解:输出 -1
- 有整数解:
- 有正整数解:输出解的个数,输出 x,y 的最小值和最大值
- 无正整数解:输出 x,y 的最小正整数值
思路:
首先是欧几里得算法,用来求 gcd(a,b),首先假设 a>b,假如 b∣a,则 gcd(a,b)=b,假如 b∤a,则:
a=kb+c
令 d∣a,d∣b,则显然有 d∣c ,令 d=gcd(a,b),则 gcd(a,b)∣c,而 c=amodb,因此 gcd(a,b)=gcd(b,amodb)。
int gcd(int a,int b){
if(!b)return a;
return gcd(b,a%b);
}
然后是扩展欧几里得,用来求 ax+by=gcd(a,b) 的一组可行解,首先令
ax1+by1bx2+(amodb)y2=gcd(a,b)=gcd(b,amodb)
根据欧几里得算法,有 gcd(a,b)=gcd(b,amodb),因此:
ax1+by1ax1+by1ax1+by1=bx2+(amodb)y2=bx2+(a−b⌊ba⌋)y2=ay2+b(x2−⌊ba⌋y2)
可得:
x1y1=y2=x2−⌊ba⌋y2
通过递归可解:
int exgcd(int a,int b,int &x,int &y){
if(!b){x=1,y=0;return a;}
int d=exgcd(b,a%b,x,y);
int t=x;x=y;y=t-(a/b)*y;
return d;
}
由此可以解出 x0,y0 使得
ax0+by0=gcd(a,b)
成立,为了解出 ax+by=c 的一个特解,对上式做一些变换,得:
agcd(a,b)x0c+bgcd(a,b)y0c=c
因此可得原方程得一个特解 x1,y1:
x1=gcd(a,b)x0c,y1=gcd(a,b)y0c
然后构造原方程通解,令 d∈Q,则
a(x1+db)+b(y1−da)=c
并且 da,db∈Z,易知 d=gcd(a,b)1,令
dx=gcd(a,b)b,dy=gcd(a,b)a
则通解为:
xy=x1+sdx=y1−sdy
其中 s∈Z。
考虑解出来的 x,y 均为正整数,则
x1+sdx>0y1−sdy>0⟹s>−dxx1⟹s<dyy1
因此
L=⌈dx−x1+1⌉≤s≤⌊dyy1−1⌋=R
假如 L>R,则不存在正整数解,s=L 时得 xmin,s=R 时得 xmax;
否则,存在正整数解,解的个数是 R−L+1,s=L 时可以得到 xmin,ymax ,s=R 时可以得到 xmax,ymin
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
int T;
ll a,b,c;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){x=1,y=0;return a;}
ll d=exgcd(b,a%b,x,y);
ll t=x;x=y;y=t-a/b*y;
return d;
}
int main(){
#ifdef WINE
freopen("data.in","r",stdin);
#endif
scanf("%d",&T);
while(T--){
ll x0,y0;
scanf("%lld%lld%lld",&a,&b,&c);
ll d=exgcd(a,b,x0,y0);
if(c%d){
printf("-1\n");
continue;
}
ll x1=x0*c/d,y1=y0*c/d;
ll dx=b/d,dy=a/d;
ll L=(ll)ceil((-x1+1)*1.0/dx);
ll R=(ll)floor((y1-1)*1.0/dy);
ll xmin=x1+L*dx;
ll ymin=y1-R*dy;
if(L>R){
printf("%lld %lld\n",xmin,ymin);
continue;
}
ll xmax=x1+R*dx,ymax=y1-L*dy;
printf("%lld %lld %lld %lld %lld\n",R-L+1,xmin,ymin,xmax,ymax);
}
return 0;
}