P5656 【模板】二元一次不定方程 (exgcd)
P5656 【模板】二元一次不定方程 (exgcd)
题目描述
给定不定方程
若该方程无整数解,输出 \(-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基础:
对于一个不定方程
其有解的充要条件其实就是 \(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)}\)
那么就能得到原方程的特解:
既$$x_0=x_0'\times q,y_0=y_0'\times q $$
然后我们考虑构造通解:
然后我们考虑最大/最小化通解\((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}\):
然后解的个数的话就是\(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;
}

浙公网安备 33010602011771号