扩展欧几里德算法

基本知识

扩展欧几里德算法即 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\)。于是通解出来了:

\[\begin{cases}x=x_1+k\times tx\\y=y_1-k\times ty\end{cases} \]

其中,\(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;
}
posted @ 2025-10-27 17:22  PengDave  阅读(12)  评论(0)    收藏  举报