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

【模板】二元一次不定方程 (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 的最大值。

读入输出量较大,注意使用较快的读入输出方式

样例 #1

样例输入 #1

7
2 11 100
3 18 6
192 608 17
19 2 60817
11 45 14
19 19 810
98 76 5432

样例输出 #1

4 6 2 39 8
2 1
-1
1600 1 18 3199 30399
34 3
-1
2 12 7 50 56

提示

【数据范围】

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

part 1

ax+by=m的必要有解条件是m mod gcd(a,b)=0
a,b同为gcd(a,b)的倍数,则axbygcd(a,b)的倍数
ax+bygcd(a,b)的倍数,所以mgcd(a,b)的倍数
其他的就正常开始用exgcd做就行,详情 [NOIP2012 提高组] 同余方程

part 2

方程可以写成a(x+kb)+b(yka)=m(拆括号消掉就行了)
而这其中的x=x1+kby=y1kaa,bb=b/gcd(a,b)a=a/gcd(a,b)
所以x>0x1+kb>0k>x1b
y>0y1ka>0k<y1a
x1b<k<y1a
k
xy
kxmaxymin
kymaxxmin

part 3(注意)

1 开long long

2 part 2中的k的左右范围在开始时是不可以取到等于的,所以防止出现smin和smax刚好被整除的情况出现要处理一下,因为分母与分子在不被整除的情况下相差一定大于等于1,所以加小于等于1的都数都行

3 x与y在刚出exgcd时是在ax+by=gcd(a,b)时候的解,所以要乘上cgcd(a,b),因为前面c处理过,所以直接乘c就行

其他相关可见我参考的博客

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
long long t , a , b , c , g , temp;
long long x , y , xmax , xmin , ymax , ymin , solution;
long long smin , smax;//记得是long long
inline long long read()
{
long long x = 0 , f = 1 ;
char ch = getchar();
while ( ch < '0' || ch > '9' ) {
if ( ch == '-' ) f *= -1;
ch = getchar();
}
while ( ch >= '0' && ch <= '9' ) {
x = ( x << 1 ) + ( x << 3 ) + ( ch - '0' );
ch = getchar();
}
return x * f;
}//快读
void exgcd( long long a_ ,long long b_ ) {
if ( b_ == 0 ){
x = 1;
y = 0;
return;
}
exgcd( b_ , a_ % b_ );
temp = x;
x = y;
y = temp - ( a_ / b_ ) * y;
return;
}
int main()
{
t = read();
while ( t -- ) {
x = 0 , y = 0;
a = read() , b = read() , c = read();
g = __gcd( a , b );
if ( c % g != 0 ) {
puts("-1");
continue;
}
a /= g , b /= g , c /= g;
//缩小精度 防止出现b过大的情况
exgcd( a , b );
//因为a(x1+kb)+b(y1-ka) = c = a*x1+kab+b*y1-kab =a*x1+b*y1
//所以通解形式是x = x1 + kb 和 y = y1 - ka
//这里得到 x > 0 和 y > 0
//x1 + kb > 0 则 k > - ( x1 / b )
//y1 - ka > 0 则 k < ( y1 / a )
//因为原式子不可取等
//所以在取等的时候前面要加上1后面减1防止出现刚好simn和smax可以整除的情况
x *= c;
y *= c;
smin = ceil ( ( (double) -x + 1.0 ) / (double)b);//这里注意除的是b
smax = floor ( ( (double) y * 1.0 - 1.0 ) / (double)a);//这里注意除的是a
smin = floor ( smin );
smax = ceil ( smax );
solution = smax - smin + 1;
xmin = x + smin * b;
ymax = (c - a * xmin) / b;
ymin = y - smax * a;
xmax = (c - b * ymin) / a;
if ( smin > smax ) printf ( "%lld %lld\n" , xmin , ymin );
else printf( "%lld %lld %lld %lld %lld\n" , solution, xmin , ymin , xmax , ymax );
}
return 0;
}
posted @   觉清风  阅读(83)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示