P5656 【模板】二元一次不定方程 (exgcd)

P5656 【模板】二元一次不定方程 (exgcd)

题目描述

给定不定方程

\[ax+by=c \]

若该方程无整数解,输出 \(-1\)
若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中 \(x\) 的最小值,所有正整数解中 \(y\) 的最小值,所有正整数解中 \(x\) 的最大值,以及所有正整数解中 \(y\) 的最大值。
若方程有整数解,但没有正整数解,你需要输出所有整数解\(x\) 的最小正整数值, \(y\) 的最小正整数值。

正整数解即为 \(x, y\) 均为正整数的解,\(\boldsymbol{0}\) 不是正整数
整数解即为 \(x,y\) 均为整数的解。
\(x\) 的最小正整数值即所有 \(x\) 为正整数的整数解中 \(x\) 的最小值,\(y\) 同理。

输入格式

第一行一个正整数 \(T\),代表数据组数。

接下来 \(T\) 行,每行三个由空格隔开的正整数 \(a, b, c\)

输出格式

\(T\) 行。

若该行对应的询问无整数解,一个数字 \(-1\)
若该行对应的询问有整数解但无正整数解,包含 \(2\) 个由空格隔开的数字,依次代表整数解中,\(x\) 的最小正整数值,\(y\) 的最小正整数值。
否则包含 \(5\) 个由空格隔开的数字,依次代表正整数解的数量,正整数解中,\(x\) 的最小值,\(y\) 的最小值,\(x\) 的最大值,\(y\) 的最大值。

【数据范围】

对于 \(100\%\) 的数据,\(1 \le T \le 2 \times {10}^5\)\(1 \le a, b, c \le {10}^9\)

说句闲话:

曾经励志永不学数论的我竟然为了写 excrt 来把这题补了qwq

说好的数据结构选手呢?(ノ▼Д▼)ノ

Solution:

exgxd基础:

对于一个不定方程

\[ax+by=c \]

其有解的充要条件其实就是 \(gcd(a,b)|c\)

我们思考一下为什么:
首先我们不难发现 \(gcd(a,b)\) 其实是 \(ax+by\) 的最小正整数取值,那么只有当 \(c\)\(gcd(a,b)\) 的倍数时,原方程 \(ax+by=c\) 才有解。

所以我们可以用exgcd轻易的构造出 \(ax+by=(a,b)\) 的一组特解 \((x_0',y_0')\),然后令 \(d=gcd(a,b),q= \frac{c}{gcd(a,b)}\)

那么就能得到原方程的特解:

\[a(x_0' \times q)+b(y_0' \times q)=d\times \frac{c}{d}=c \]

既$$x_0=x_0'\times q,y_0=y_0'\times q $$

然后我们考虑构造通解:

\[ \left\{ \begin{aligned} x=x_0+n\\\\ y=y_0+m \end{aligned} \right. \]

\[a(x_0+n)+b(y_0+m)=c \]

\[a(x_0)+b(y_0)=c \]

\[a\times n+b\times m=0 \]

\[ \left\{ \begin{aligned} n=t\times \frac{b}{d}\\\\ m=-t\times \frac{a}{d} \end{aligned} \right. \]

\[ \left\{ \begin{aligned} x=x_0+t\times \frac{b}{d}\\\\ y=y_0+-t\times \frac{a}{d} \end{aligned} \right. \]

然后我们考虑最大/最小化通解\((x,y)\),我们设最小增量分别为 \(tx=\frac{b}{d},ty=\frac{a}{d}\)

\(x_0+k\times tx\ge1\)
\(k\ge \lceil{\frac{1-x_0}{tx}} \rceil\)

然后你在感性理解一下x,y的关系应该是 "此消彼长"
的,那么当 \(x\) 取最小时,\(y\) 取最大,反之亦然。

然后我们已经求出了 \(min_x,max_y\),我们现在要求剩下的两个:

回顾一下我们的通解:\(y=y_0+k\times ty\)

\(y_{min}=y_0+k_{min}\times ty\)
\(y_{max}=y_0+k_{max}\times ty\)
\(y_{min}=y_{max}-(k_{max}-k_{min})\times ty\)

所以我们只需要设 \(tmp=k_{max}-k_{min}\):

\[ \left\{ \begin{aligned} x_{max}=x_{min}+tmp\times tx\\ y_{min}=y_{max}-tmp\times ty \end{aligned} \right. \]

然后解的个数的话就是\(tmp+1\)

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll read()
{
    ll res=0,flag=0;
    char c=getchar();
    while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
    while('0'<=c&&c<='9'){res=(res<<3)+(res<<1)+c-'0';c=getchar();}
    return (flag ? -res : res);
}
ll exgcd(ll &x,ll &y,ll a,ll b)
{
    if(b==0)
    {
        x=1,y=0;return a;
    }
    ll res=exgcd(y,x,b,a%b);
    y-=a/b*x;
    return res;
}
ll a,b,c,x,y;
void work()
{
    a=read(),b=read(),c=read();
    ll d=exgcd(x,y,a,b),q=c/d;
    if(c%d){printf("%d\n",-1);return;}
    x*=q,y*=q;
    ll tx=b/d,ty=a/d,k=ceil((1.0-x)/tx);
    x+=tx*k,y-=ty*k;
    if(y<=0)
    {
        ll ymin=y+ty*1ll*ceil((1.0-y)/ty);
        printf("%lld %lld\n",x,ymin);
        return;
    }
    ll tmp=(y-1)/ty;
    printf("%lld ",tmp+1);
    printf("%lld %lld ",x,y-tmp*ty);
    printf("%lld %lld\n",x+tmp*tx,y);
}
int main()
{
    //freopen("exgcd.in","r",stdin);
    //freopen("exgcd.out","w",stdout);
    int T;
    cin>>T;
    while(T--)work();
    return 0;
}
posted @ 2025-02-07 18:38  liuboom  阅读(48)  评论(0)    收藏  举报