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;
}