扩展欧几里德算法
基本知识
扩展欧几里德算法即 exgcd,一般用来求形如 \(ax+by=\gcd(a,b)\) 的二元一次不定方程的整数解。以下为了方便将用 \(d\) 代替 \(\gcd(a,b)\)。
考虑 \(\gcd(b,a\bmod b)=\gcd(a,b)\),,于是递归,边界是 \(b=0\),此时方程是 \(ax=a\),显然 \(x=1\),\(y\) 没有限制。
再考虑 \(bx+(a\bmod b)y=d\) 的解如何转变成 \(ax+by=d\) 的,我们设 \(bx+(a\bmod b)y=d\) 的解 \(x=y_0,y=x_0\),于是 \((a\bmod b)x_0+by_0=d\),因此我们可以令 \(ax+by=d\) 的解 \(x=x_0\),注意到 \(a=b\times\lfloor \frac{a}{b}\rfloor+a\bmod b\),因此 \(ax+by=d\) 和 \((a\bmod b)x_0+by_0=d\) 两式相减得 \(b\times\lfloor \frac{a}{b}\rfloor x_0+b(y-y_0)=0\),变一下形得 \(y=y_0-\lfloor \frac{a}{b}\rfloor x_0\)。
这样的话,代码就不难写了:
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1;y=0;
return a;
}ll d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
P5656 【模板】二元一次不定方程 (exgcd)
考虑 exgcd,后面将用 \(d\) 表示 \(\gcd(a,b)\)。
首先用 exgcd 求 \(ax+by=d\) 一组解 \(x_0,y_0\)。考虑根据裴蜀定理,如果 \(c\) 不是 \(d\) 的倍数,则必定无解。于是对于 \(ax+by=c\),我们得到了一组解 \(\begin{cases}x_1=x_0\times \frac{c}{d}\\y_1=y_0\times\frac{c}{d}\end{cases}\)。
接下来求通解,令 \(a(x_1+n)+b(y_1+m)=c\),拆开后得 \(ax_1+by_1+an+bm=c\),注意到 \(ax_1+by_1=c\),于是 \(an+bm=0\)。由于 \(a,n,b,m\) 都是整数,因此绝对值最小的 \(n,m\) 当然是凑 \(a,b\) 的最小公倍数(也就是 \(\frac{ab}{d}\)),也就是使得 \(an+bm=\frac{ab}{d}-\frac{ab}{d}=0\),因此 \(n=\frac{b}{d},m=\frac{a}{d}\)。这里为了方便辨识,令 \(tx=n,ty=m\)。于是通解出来了:
其中,\(k\in \mathbb{Z}\)。
这样,后面的就好求了。我们先求 \(x\) 的最小正整数解,此时有 \(x_{\max}+k\times tx\ge 1\),变一下形得 \(k\ge\lceil\frac{1-x_{\max}}{tx}\rceil\),同时,由于 \(x\) 达到了最小,那 \(y\) 就达到了最大,如果这时的 \(y_{\max}\) 仍不为正,那么就无正整数解,其中 \(y\) 的最小正整数解也能用同样的方法求出。
如果有正整数解,我们考虑这个最大的 \(y_{\max}\),设最小的为 \(y_{\min}\),则 \(y_{\min}=y_{\max}-k\times ty\ge 1\),得 \(k\le\lfloor\frac{y_{\max}-1}{ty}\rfloor\),于是我们也得到解的数量为 \(\lfloor\frac{y_{\max}-1}{ty}\rfloor+1\),同理也能求出 \(x_{\max}\)。
这样代码也不难写了:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1;y=0;
return a;
}ll d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
signed main(){
cin.tie(0);ios::sync_with_stdio(0);
int T;cin>>T;
while(T--){
ll a,b,c;cin>>a>>b>>c;int x,y;
ll d=exgcd(a,b,x,y);
if(c%d){
cout<<"-1\n";continue;
}
x=x*(c/d),y=y*(c/d);
ll tx=b/d,ty=a/d;
ll k=ceil((1.0-x)/tx);
x+=tx*k;y-=ty*k;
if(y<=0){
cout<<x<<' '<<y+ty*(long long)ceil((1.0-y)/ty)<<'\n';
}else{
cout<<(y-1)/ty+1<<' '<<x<<' '<<(y-1)%ty+1<<' '<<x+(y-1)/ty*tx<<' '<<y<<'\n';
}
}
return 0;
}

浙公网安备 33010602011771号