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

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

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

样例 #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\%\) 的数据,\(1 \le T \le 2 \times {10}^5\)\(1 \le a, b, c \le {10}^9\)

part 1

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

part 2

方程可以写成\(a(x+kb)+b(y-ka)=m\)(拆括号消掉就行了)
而这其中的\(x=x_1+kb和y=y_1-ka中a,b的最小时b=b/gcd(a,b) a=a/gcd(a,b)\)
所以\(当x>0则x_1+kb>0所以k>-\frac{x_1}{b}\)
\(当y>0则y_1-ka>0所以k<\frac{y_1}{a}\)
\(-\frac{x_1}{b}<k<\frac{y_1}{a}\)
\(若k为空集则无正整数解\)
\(因为x与y是呈反比例的关系\)
\(所以由上述可知在取k的左端点时存在x的max和y的min\)
\(取k的右端点时存在y的max和x的min\)

part 3(注意)

1 开long long

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

3 x与y在刚出exgcd时是在\(ax+by=gcd(a,b)\)时候的解,所以要乘上\(\frac{c}{gcd(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 @ 2023-02-18 22:19  觉清风  阅读(46)  评论(0编辑  收藏  举报