zoj3593 扩展欧几里德

副标题:以为可以脱非扩欧,奈何败于智商不足

题目分析

首先这个 c=a+b ,所以我们可以暂时只考虑 a b,那么假设 L=abs(BA) ,则就是要解这个方程: ax+by=c ,这个是扩欧的事情,无解的判断与扩欧一样,不多叙述。
但是解出了一个解之后是不够的,还要求最优解。
容易意会得到,如果 a=a/gcd(a,b) , b=b/gcd(a,b) ,那么通过扩欧可以得到一个特解 x0 y0 ,然后通解可以表示为 x=x0+at y=y0bt
可以在坐标系上画出两条直线 l1,l2 (注意上式中的 x 不同于坐标系的x轴,x轴准确的说应该是t轴!),并且斜率是一正一负(a,b同正,题目已说)
所以呢,取一个t,如果 x y异号,答案就是你画一条平行于y轴的直线,它和 l1,l2 的交点间的距离。如果同号就是在这两条直线上横坐标为 t 的离x轴远的那个点离x轴的距离,那么意会可得,l1,l2交点处取到最优解,由于交点处的 t <script type="math/tex" id="MathJax-Element-3271">t</script>不一定是整数,所以我们要做一些处理(见代码)

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<climits>
using namespace std;
#define ll long long
ll read(){
    ll q=0,w=1;char ch=' ';
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')q=(ll)q*10+(ll)(ch-'0'),ch=getchar();
    return q*w;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){x=1;y=0;return a;}
    ll d=exgcd(b,a%b,x,y),tmp;
    tmp=x;x=y;y=tmp-(a/b)*y;
    return d;
}
ll s,t,a,b,T,mi;
int main()
{
    ll x,y,d,L,i;
    T=read();
    while(T--){
        s=read();t=read();a=read();b=read();
        d=exgcd(a,b,x,y);L=abs(t-s);
        if(L%d){printf("-1\n");continue;}
        ll ans=LLONG_MAX,kl;
        x=x*(L/d);y=y*(L/d);a/=d;b/=d;
        mi=(y-x)/(a+b);//交点
        for(i=mi-1;i<=mi+1;i++){//讨论整点
            if(fabs(x+b*i)+fabs(y-a*i)==fabs(x+b*i+y-a*i))
                kl=max(x+b*i,y-a*i);
            else kl=fabs(x-y+(a+b)*i);//是加号QAQ
            ans=min(ans,kl);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值