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

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

题目描述

给定不定方程

a x + b y = c ax+by=c ax+by=c

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

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

输入格式

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

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

输出格式

T T T 行。

若该行对应的询问无整数解,一个数字 − 1 -1 1
若该行对应的询问有整数解但无正整数解,包含 2 2 2 个由空格隔开的数字,依次代表整数解中, x x x 的最小正整数值, y y y 的最小正整数值。
否则包含 5 5 5 个由空格隔开的数字,依次代表正整数解的数量,正整数解中, x x x 的最小值, y y y 的最小值, x x x 的最大值, y y 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 % 100\% 100% 的数据, 1 ≤ T ≤ 2 × 10 5 1 \le T \le 2 \times {10}^5 1T2×105 1 ≤ a , b , c ≤ 10 9 1 \le a, b, c \le {10}^9 1a,b,c109

使用扩展欧几里得算法求出特解和通解,用通解的大于等于1的不等式计算得到k的取值范围,根据一元函数的性质进行计算最值即可

#include<iostream>
using namespace std;

typedef long long ll;
ll a, b, c, T;

ll exgcd(ll a, ll b, ll& x, ll& y){
	if(b == 0){
		x = 1;
		y = 0;
		return a;
	}
	ll x1;
	ll g = exgcd(b, a%b, x1, x);
	y = x1-a/b*x;
	return g;
}

inline ll read(){
	char ch = getchar();
	ll x = 0;
	int f = 1;
	while(ch < '0' || ch > '9'){
		if(ch == '-') f = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		x = x*10+ch-'0';
		ch = getchar();
	}
	return x*f;
}

int main(){
	
	cin >> T;
	while(T--){
		a = read(), b = read(), c = read();
		ll x, y, g;
		g = exgcd(a, b, x, y);
		if(c % g != 0){
			cout << -1 << endl;
			continue;
		}
		x = x*c/g, y = y*c/g;
		ll l = (1-y)*g/a, r = (x-1)*g/b;        //这个过程很重要
		if((x-1)*g % b != 0 && x < 1) r--; //判断一个式子正负要用过程可能为负的判断,因为结果会自动取整
		if((1-y)*g % a != 0 && y < 1) l++;//比如-2/3 = 0
//		while(y+l*a/g <= 0)l++; //这样写也可以
//  	while(x-r*b/g <= 0)r--;
		if(r-l+1 <= 0){
			printf("%lld %lld\n", x-r*b/g, y+l*a/g);
		}
		else{
			printf("%lld %lld %lld %lld %lld\n", r-l+1, (x-r*b/g), (y+l*a/g), (x-l*b/g), (y+r*a/g));
		}	
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值