UVALive 6428 A+B 【扩展欧几里得】

该博客主要讨论UVALive 6428题目的解决方案,涉及数学中的扩展欧几里得算法。题目要求通过特定操作使得a或b位置上的数值达到S。博主发现只要a和b的系数满足最大公约数为1,即可达成目标。然而在实现过程中,由于未注意a、b、S的大范围(0~10^18),导致运行错误。博主分享了使用扩展欧几里得解决不定方程的模板,并指出在求解最小x值时,需要避免整型溢出,正确做法是(x%c%d)%B来确保结果在合法范围内。
摘要由CSDN通过智能技术生成

题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4439

题目大意:给出a,b,S三个数,每次可以选择从a的位置加到b上,也可以从b的位置加到a上,问a或者b的位置上能否达到S。

   比如:给出a和b,可以得到的是

    (a b) 

  (a+b b) (a a+b) 

      (a+2b b)   (a+b a+2*b)     (2a+b a+b)      (a 2a+b)

......

    只要符合上面的a和b的系数关系即可。

推了三行,发现了系数x和y只要满足gcd(x,y)==1即可。

然后就RE了,读题不细心:a,b,S的范围是[ 0~10^18 ]。 除了0,导致了RE。

后来就一直WA,为啥呢..........

用的扩展欧几里得求不定方程的解的模板:

LL gcd(LL a,LL b){
    if(b==0) return a;
    else return gcd(b,a%b);
}

LL ex_gcd(LL a,LL b,LL &x,LL &y){
    if(b==0){
        x=1;y=0;
        return a;
    }
    LL r=ex_gcd(b,a%b,x,y);
    LL t=x;
    x=y;
    y=t-a/b*y;
    return r;
}

//求解ax+by=c的解x,y x,y需要提前定义
bool linear_equation(LL a,LL b,LL c,LL &x,LL &y)
{
    LL d=ex_gcd(a,b,x,y);
    if(c%d!=0)   //即不整除
        return false;
    LL k=c/d;
    x*=k; y*=k;    //求得的只是其中一组解,另外的解是:x+b/gcd(a,b)*i y-a/gcd(a,b)*i
    return true;
}

注意最后三行,x=x*c/d。

而最后我要求一个x的最小的值。

用了(x%B+B)% B

所以来说就是(x*c/d)% B  而这个值就是有可能爆LL。所以一直wa。

要变成   x = (x%B)* (c / d %B)

#include<iostream>
#include<stdio.h>
using namespace std;
#define LL long long

LL gcd(LL a,LL b){
    if(b==0) return a;
    else return gcd(b,a%b);
}


LL ex_gcd(LL a,LL b,LL &x,LL &y){
    if(b==0){
        x=1;y=0;
        return a;
    }
    LL r=ex_gcd(b,a%b,y,x);
    y=y-a/b*x;
    return r;
}


//求解ax+by=c的解x,y x,y需要提前定义
bool linear_equation(LL a,LL b,LL c,LL &x,LL &y)
{
    LL d=ex_gcd(a,b,x,y);
    if(c%d!=0)   //即不整除
        return false;
    //LL k=c/d;
    //x*=k; y*=k;    //求得的只是其中一组解,另外的解是:x+b/gcd(a,b)*i y-a/gcd(a,b)*i
    return true;
}


int main (){
    LL a,b,S;
    while(~scanf("%lld%lld%lld",&a,&b,&S)){
        if(a==S||b==S)          {printf("YES\n");continue;}
        if(a==0&&b==0)          {printf("NO\n");continue;}

        if(a==0&&S%b==0)        {printf("YES\n");continue;}
        if(a==0&&S%b!=0)        {printf("NO\n");continue;}

        if(b==0&&S%a==0)        {printf("YES\n");continue;}
        if(b==0&&S%a!=0)        {printf("NO\n");continue;}


        int flag = 0;
        LL x,y;
        LL i = 1;
        LL g = gcd(a,b);
        if(linear_equation(a,b,S,x,y)) {
            //printf("%lld %lld\n",x,y);
           LL B = b/g;
           LL A = a/g;
           x = (S / g % B) * (x % B);
           x = (x % B + B) % B;
           y = (S - (x*a) )/b;
            while(1){
                 if( y <= 0 ) {break;}
                 if((x>0&&y>0&&gcd(x,y)==1)) {flag = 1; break;}
                 x = x + B;
                 y = y - A;
                //printf("%lld %lld\n",x,y);
            }
        }
        else{
            printf("NO\n"); continue;
        }

        if(flag==1) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
/*
1 2 3
3 4 5
3 4 17
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值