[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。
若该行对应的询问有整数解但无正整数解,包含 22 个由空格隔开的数字,依次代表整数解中,x的最小正整数值,y的最小正整数值。
否则包含 5 个由空格隔开的数字,依次代表正整数解的数量,正整数解中,x 的最小值,y 的最小值,x 的最大值,y的最大值。
题解
由线性同余方程相关定理以及裴蜀定理(贝祖定理)可知,当且仅当gcd(a,b)|c时,方程有整数解。令d = gcd(a,b),
x
0
=
d
x
c
,
y
0
=
d
y
c
x_0=\frac{dx}{c},y_0=\frac{dy}{c}
x0=cdx,y0=cdy
a
x
0
+
b
y
0
=
d
①
ax_0 + by_0 = d\ \ ①
ax0+by0=d ①
由扩展欧几里得算法可以得出方程①的一组解为
x
0
,
y
0
x_0,y_0
x0,y0
→ x = x 0 c d , y = y 0 c d \to x = \frac{x_0c}{d},y=\frac{y_0c}{d} →x=dx0c,y=dy0c
但是这求出的仅仅是一组特殊解,显然此实还不能够满足题目的要求。这时我们就需要将x,y进行放大和缩小。假设将x增大p,将y减小q。
{
a
(
x
+
p
)
+
b
(
y
−
q
)
=
c
a
x
+
b
y
=
c
→
p
=
b
∗
q
a
,
q
=
a
∗
p
b
\begin{cases} a(x+p) + b(y-q) = c\\ ax + by = c \end{cases} \to p =\frac{b*q}{a},q=\frac{a*p}{b}
{a(x+p)+b(y−q)=cax+by=c→p=ab∗q,q=ba∗p
我们想要p,q为最小的正整数,那么必然有a|b*q且b|b*q,即KaTeX parse error: Got function '\min' with no arguments as subscript at position 7: (b*q)_\̲m̲i̲n̲=\frac{a*b}{gcd…
KaTeX parse error: Got function '\min' with no arguments as subscript at position 21: …in{cases} p_\̲m̲i̲n̲ ̲= \frac{b}{d}\\…
那么下面就可以对x,y进行放大缩小:
将x变为最小正整数,同时改变y的值。
令 x + k p ≥ 1 → k ≥ ⌈ ( 1 − x ) ⌉ p x + kp ≥ 1\to k≥\frac{\lceil(1-x)\rceil}{p} x+kp≥1→k≥p⌈(1−x)⌉
那么x的最小正整数为x+=k*p,对应的y+=k*q(x为正整数时的最大整数解)
无整数解
无整数解的情况上面已经特判,即c%d !=0
有正整数解
正整数解的个数: y − 1 q + 1 \frac{y-1}{q}+1 qy−1+1
最小正整数解: x , y − y − 1 q ∗ q x,y-\frac{y-1}{q}*q x,y−qy−1∗q
最大正整数解: x + y − 1 q ∗ p , y x+\frac{y-1}{q}*p,y x+qy−1∗p,y
无正整数解但是有整数解
最小正整数解: x , y + q ∗ ⌈ 1 − y ⌉ q x,y+q*\frac{\lceil1-y\rceil}{q} x,y+q∗q⌈1−y⌉
#include<cstdio>
#include<cmath>
using namespace std;
#define int long long
int ex_gcd(int a,int b,int &x,int &y){ //扩展欧几里得算法
if(b==0){
x=1;
y=0;
return a;
}
int d = ex_gcd(b,a%b,x,y);
int temp = x;
x = y;
y = temp - a/b*y;
return d;
}
signed main(){
int n;
int a,b,c,x,y;
scanf("%lld",&n);
while(n--){
scanf("%lld%lld%lld",&a,&b,&c);
int d = ex_gcd(a,b,x,y);
if(c%d){ //无解的情况
printf("-1\n");
continue;
}
x*=c/d,y*=c/d;
int p = b/d,q = a/d;
if(x<0) k=ceil((1.0-x)/p);
else k=(1-x)/p;
x+=k*p,y-=k*q;
if(y>0){ //有正整数解的情况
printf("%lld ",(y-1)/q+1);
printf("%lld ",x);
printf("%lld ",y-q*(y-1)/q);
printf("%lld ",x+p*(y-1)/q);
printf("%lld\n",y);
}else{ //无正整数解的情况
printf("%lld ",x);
printf("%lld\n",y+ceil((1.0-y)/q));
}
}
return 0;
}