POJ1061 青蛙的约会 扩展欧几里得

CSU2021 Exponial


题面:

  • 一个数轴上面有x,y两点,数轴长 l 且首尾相连。
  • 两个点分别可以同时移动 m,n 每次(方向一定)。
  • 问是否可以移动到同一点。可以的话输出移动的最少次数,不可以的输出”Impossible”。

解题思路:

解题思路转载DQSSS的博客

说白了就是让你求这个(解a):

                            x+am≡y+an(modL)
它就等于

                            a(m−n)≡y−x(modL)
把模去掉,就等于

                            a(m−n)+Lk=y−x
然后,用exgcd求

                            a(m−n)+Lk=gcd(m−n,L)
设d=gcd(m-n,L),c=y-x。

若c%d!=0,则无解。

这样解出a后,最终答案就是:

                            (a∗cd)modLd
关于最后一步的证明,我是这样想的:

设要解的方程(求x)是:

                                ax1+by1=c
而我们已经解得

                                ax+by=gcd(a,b)=d
此时将第二个方程左右同时乘c/d,则可得:

                            ax∗cd+by∗cd=c
所以:

                                x1=x∗cd
这样并没有完,因为这只是一组解,我们要求最小正整数解。

我们知道:若一组 < x,y > 是ax+by=c的一组解,那么

                            <x−bd,y+ad>
也是原方程的一组解。

这样我们只需要让解得的x不断减b/d,直到再减就为负数时,所得的x就是我们要的解。 
其实这个过程就是模运算,所以最小正整数解就是:

                            x1=(x∗cd)modbd
还有一种证法。对于这个式子:

                                ax1+by1=c
我们可以让等式两边同时除以d,则:

                            adx1+bdy1=cd
相当于化简,此时对结果无影响,求就好了。

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
using namespace std;
#define rep(i,l,p) for(int i=l;i<=p;i++)
#define fread() freopen("in.txt","r",stdin)
#define sf scanf
#define pf printf
typedef long long ll;
ll x,y,m,n,l;
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0) {x=1,y=0;return a;}
    ll d = exgcd(b,a%b,x,y);
    ll z = x; x = y; y = z - y * (a / b);
    return d;
}

int main(int argc, char const *argv[])
{
    while(~sf("%lld %lld %lld %lld %lld",&x,&y,&m,&n,&l)){
        ll s,t;
        if(m<n) swap(m,n),swap(x,y);
        ll d = exgcd(m-n,l,s,t);

        if((y-x)%d != 0) pf("Impossible\n");
        else{
            s = ( s*(y-x)/d %(l/d) + l/d )%(l/d);
            pf("%lld\n",s);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Hagtaril/article/details/81747635