sgu 141 Jumping Joe 扩展GCD

10 篇文章 0 订阅

    一维的数轴上,初始位置在原点,四种移动方式,左移a,右移a,左移b,右移b,问是否可以恰好K步移动到点P,若可以择输出一种可行的方案。

      实际上就是解方程

   ax+by=P,abs(x)+abs(y)=K

      用扩展GCD可以求出一组解,但这组解的绝对值的和不一定是最小的,那么可以通过x+=k*b/gcd,y-=k*a/gcd来尝试收缩出一组绝对值的和最小的解,之后记录res=K-abs(x)-abs(y),如果res是偶数,那么左右来回移动用完剩下的步子就行,奇数的话,讨论a/gcd+b/gcd的奇偶性,如果是奇数,那么可以一次收缩达成上一中情况,否则就是无解了。

另外这题一定注意特判...比如P=0的时候,扩展GCD是搞不出来的,要求一下LCM来得到初始的x,y,之后就一样了。P,K同时等于0的时候直接输出四个0....

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
typedef long long ll;
ll xx,yy,x,y,tx,ty,px,py;
ll p,k;
bool neg;
ll gcd(ll a,ll b,ll &x,ll &y)
{
    if (b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll d=gcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-a/b*y;
    return d;
}
ll lx,ly,rx,ry;
int main()
{
//    freopen("in.txt","r",stdin);
    cin>>xx>>yy>>p>>k;
    ll g=gcd(xx,yy,x,y);
    if (p % g!=0)
    {
        cout<<"NO"<<endl;
        return 0;
    }
    if (p)
    {
    x=x*p/g;
    y=y*p/g;
    }
    else
    {
        x=yy/g;
        y=-xx/g;
    }

    px=xx/g;
    py=yy/g;

    while (abs(x+py)+abs(y-px)<abs(x)+abs(y))
    {
        x+=py;
        y-=px;
    }
    while (abs(x-py)+abs(y+px)<abs(x)+abs(y))
    {
        x-=py;
        y+=px;
    }

    if (abs(x)+abs(y)>k)
    {
        cout<<"NO"<<endl;
        return 0;
    }
    ll res=k-abs(x)-abs(y);
    if (res%2==0)
    {
        if (x>0) rx=abs(x),lx=0;
        else rx=0,lx=abs(x);
        if (y>0) ry=abs(y),ly=0;
        else ry=0,ly=abs(y);
        lx+=res/2;
        rx+=res/2;
        cout<<"YES\n";
        cout<<rx<<" "<<lx<<" "<<ry<<" "<<ly<<endl;
        return 0;
    }
    if ( abs(px+py)%2==0)
    {
        cout<<"NO"<<endl;
        return 0;
    }
    else
    {
        if (abs(x+py)+abs(y-px)<abs(x-py)+abs(y+px))
        {
            x+=py;
            y-=px;
        }
        else
        {
            x-=py;
            y+=px;
        }
        res=k-abs(x)-abs(y);
        if (res<0)
        {
            cout<<"NO"<<endl;
            return 0;
        }
        if (x>0) rx=abs(x),lx=0;
        else rx=0,lx=abs(x);
        if (y>0) ry=abs(y),ly=0;
        else ry=0,ly=abs(y);
        lx+=res/2;
        rx+=res/2;
        cout<<"YES\n";
        cout<<rx<<" "<<lx<<" "<<ry<<" "<<ly<<endl;
        return 0;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值