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

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

题目描述

给定不定方程

ax+by=c

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

正整数解即为 x,y 均为正整数的解,0 不是正整数
整数解即为 x,y 均为整数的解。
x 的最小正整数值即所有 x 为正整数的整数解中 x 的最小值,y 同理。

输入格式

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

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

输出格式

T 行。

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

【数据范围】

对于 100% 的数据,1T2×1051a,b,c109

说句闲话:

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

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

Solution:

exgxd基础:

对于一个不定方程

ax+by=c

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

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

所以我们可以用exgcd轻易的构造出 ax+by=(a,b) 的一组特解 (x0,y0),然后令 d=gcd(a,b),q=cgcd(a,b)

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

a(x0×q)+b(y0×q)=d×cd=c

x0=x0×q,y0=y0×q

然后我们考虑构造通解:

{x=x0+ny=y0+m

a(x0+n)+b(y0+m)=c

a(x0)+b(y0)=c

a×n+b×m=0

{n=t×bdm=t×ad

{x=x0+t×bdy=y0+t×ad

然后我们考虑最大/最小化通解(x,y),我们设最小增量分别为 tx=bd,ty=ad

x0+k×tx1
k1x0tx

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

然后我们已经求出了 minx,maxy,我们现在要求剩下的两个:

回顾一下我们的通解:y=y0+k×ty

ymin=y0+kmin×ty
ymax=y0+kmax×ty
ymin=ymax(kmaxkmin)×ty

所以我们只需要设 tmp=kmaxkmin:

{xmax=xmin+tmp×txymin=ymaxtmp×ty

然后解的个数的话就是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 @   liuboom  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示