二元一次不定方程 (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

代码内容

// #include <iostream>
// #include <algorithm>
// #include <cstring>
// #include <stack>//栈
// #include <deque>//队列
// #include <queue>//堆/优先队列
// #include <map>//映射
// #include <unordered_map>//哈希表
// #include <vector>//容器,存数组的数,表数组的长度
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

//扩展欧几里得算法(裴蜀定理)
//根据费马定理,任意正整数a, b都存在整数x, y
//使得ax + by = gcd(a, b)
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1,y=0;
        return a;
    }
    ll d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}

int main()
{
    ll T;
    cin>>T;

    while(T--)
    {
        ll a,b,c;
        cin>>a>>b>>c;

        ll x,y;
        //扩展欧几里得算法
        //ax+by=gcd(a,b)=d
        /*(c/d)*(a*x+b*y)=d*(c/d)*/
        //a*x≡c(mod b)
        //a*x=b*y+c
        /*a*x+b*y=c*/
        ll d=exgcd(a,b,x,y);

        if(c%d)//无整数解
            cout<<-1<<endl;
        else
        {
            x*=c/d,y*=c/d;//还原

            ll p,q,k;
            //x 扩大,即变成 x+p
            //y 缩小,即变成 y-q
            p=b/d,q=a/d;

            //求最小的k,使得 x+k∗p≥1 移项
            //可得:k>ceil((1-x)/p)
            if(x<0)//将x提高到最小正整数
            {
                k=ceil((1.0-x)/p);
                //构造出一组船新的特解
                x+=p*k,y-=q*k;
            }
            else if(x>=0)//将x降低到最小正整数 
            {
                k=(x-1)/p;
                //构造出一组船新的特解
                x-=p*k,y+=q*k;
            }

            if(y>0)//有正整数解
            {
                //将y减到1的方案数即为解的个数
                cout<<(y-1)/q+1<<" ";
                //当前的x即为最小正整数x
                cout<<x<<" ";
                //将y取到最小正整数
                cout<<(y-1)%q+1<<" ";
                //将x提升到最大
                cout<<x+(y-1)/q*p<<" ";
                //特解即为y最大值
                cout<<y<<endl;;
            }
            else//无正整数解
            {
                //当前的x即为最小正整数x
                cout<<x<<" ";
                //将y提高到最小正整数
                cout<<y+q*(ll)ceil((1.0-y)/q)<<endl;
            }
        }
    }
    
    return 0;
}
  • 27
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慧狗仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值