五指山(扩展欧几里得)

五指山

题目链接
大圣在佛祖的手掌中。
我们假设佛祖的手掌是一个圆圈,圆圈的长为 n,逆时针记为:0,1,2,…,n−1,而大圣每次飞的距离为 d。
现在大圣所在的位置记为 x,而大圣想去的地方在 y。
要你告诉大圣至少要飞多少次才能到达目的地。
注意:孙悟空的筋斗云只沿着逆时针方向翻。

输入格式

有多组测试数据。
第一行是一个正整数 T,表示测试数据的组数;
每组测试数据包括一行,四个非负整数,分别为如来手掌圆圈的长度 n,筋斗所能飞的距离 d,大圣的初始位置 x 和大圣想去的地方 y。

输出格式

对于每组测试数据,输出一行,给出大圣最少要翻多少个筋斗云才能到达目的地。
如果无论翻多少个筋斗云也不能到达,输出 Impossible。

数据范围

2<n<109,
0<d<n,
0≤x,y<n

输入样例:

2
3 2 0 2
3 2 0 1

输出样例:

1
2

算法分析

该题是采用扩展欧几里得算法的应用.
扩展欧几里得说的是对于任意正整数对(a,b),一定存在非零整数x和y,使得 ax+by=gcd(a,b);
在求解的过程中当 b==0的时候 a=gcd(a,b) 此时返回
x=1,y=0,gcd(a,b)=a.
当b!=0 就递归gcd(b,a%b,y,x),递归这个的时候y的系数发生改变
a=a/b+a%b所以 a mod b == a-a / b * b
by+(a mod b)x = gcd(a,b)
->by+(a - a/b * b)x = gcd(a,b)
->ax+b(y - a/b * x) = gcd(a,b)
所以y减去一个a/bx.
扩展欧几里得还有一个应用就是求出一个x0,y0剩下的x,y就可以表示为
x == x0 + k * b/gcd(a,b) , y == y0 - k * a / gcd(a,b).这里的k可正可负.
回到这题,由题意知 (x +b * d)%n == y => x + b * d == y+a
n. a,b为系数
转换后为 b * d - a * n == y-x
d,n,y,x知道,就差 a和b了,这就用到了扩展欧几里得.这里的y-x不一定是gcd(n,b),所以我们要判断(y-x)%gcd(n,b)是否为0.
如果为0的话,我们就可以左右扩大倍数来满足等式.我们要求b 那么b就需要被扩大(y-x)/gcd(n,b)倍.
又b要求为最小的正整数 b == b0 + k * n/gcd(n,b)
所以(b%n/gcd(n,b) +n/gcd(n,b))%n/gcd(n,b)就可以确保是最小的正整数

代码实现

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n,d,x,y,t;
int exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    ll d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>d>>x>>y;
        ll a,b;
        int gcd=exgcd(n,d,a,b);
        if((y-x)%gcd!=0)    cout<<"Impossible"<<endl;
        else
        {
            b*=(y-x)/gcd;
            n/=gcd;
            cout<<(b%n+n)%n<<endl;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值