在上这道题之前,先给大家看个精彩的:
这道玄学的题目,让我整整花费了八个小时去推导以及去了解拓展欧几里得,从一个萌新到真正可以理清自己的思路了。
1 2 3 4 5Sample Output
4
这是学长挂给我们的一道题,重在让我们理清欧几里得算法的作用以及灵活运用它。
思路:遇上这道题,我们先列写出方程,我们可以知道:x+m*t-y-n*t=p*L(p为转了几圈)。
很显然,这是一道拓展欧几里得算法,然后我们针对这道题开始讲解什么是欧几里得算法:
假如存在这样的a*x+b*y=d的线性方程,我们想知道它的通解怎样实现,那么我们就举个例子吧:
a*x+b*y=d,如果d mod gcd(a, b)==0
则,a*x1+b*y1=d; b*x2+a%b*y2=d; 整理得b*x2+(a-(a/b)*b)*y2=d;
于是一一对应前式a、b所对应点有,x1=y2; y1=x2-(a/b)*y2。
那么,我们由此可以写出拓展欧几里得公式:
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll g=exgcd(b, a%b, x, y);
ll temp=x;
x=y;
y=temp-y*(a/b);
return g;
}
当然,这是一种四个参数的,还有种五个参数的:
void exgcd(ll a, ll b, ll &d, ll &x, ll &y)
{
if(b==0)
{
d=a;
x=1;
y=0;
return;
}
exgcd(b, a%b, d, y, x);
y-=a/b*x;
}
现在,我们要做一个判断:方程是否有解?
拓展欧几里得公式存在,就是指a*x+b*y=d;而d%gcd(a, b)==0,那么假如d%gcd(a, b)!=0,则等式不成立。
判断条件:
if(d%gcd(a, b)!=0) 不存在解。
寻找结果answer:
直接输出 exgcd(a, b, &x, &y)中的x?当然是不行的,我们求得的x为其中的一个特解,不一定是最佳的答案,而且还有负值的可能,所以我们要做些处理:
对于一个x数取到0~r中的值,我们可以这么操作:x=((x%r)+r)%r。
那么容我证明一下这个r的取值:
a*x0+b*y0=d; 又假设还存在这样的x1、y1:a*x1+b*y1=d;
则,a*(x1-x0)=b*(y0-y1); a/gcd(a, b)*(x1-x0)=b/gcd(a, b)*(y0-y1);
a/gcd(a, b)与b/gcd(a, b)互质,则(x1-x0)与b/gcd(a, b)成正比,(y0-y1)与a/gcd(a, b)成正比。
所以能得到:x=x0+K*b/gcd(a, b)其中K为一切整数,y=y0+K*a/gcd(a, b)其中K为一切整数。
那么,对于x而言,r=b/gcd(a, b)。
ll want(ll x0, ll r0)
{
return ((x0%r0)+r0)%r0;
}
捋通了思路之后,我们就可以知道了:我们对于公式:(n-m)*t+L*P=x-y;分别将他们看作线性方程的a,b,x,y,即可求得正解。
完整代码:
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b)
{
if(b==0)return a;
return gcd(b, a%b);
}
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 g=exgcd(b, a%b, x, y);
ll temp=x;
x=y;
y=temp-y*(a/b);
return g;
}
ll want(ll x0, ll r0)
{
return ((x0%r0)+r0)%r0;
}
int main()
{
while(scanf("%lld %lld %lld %lld %lld",&x,&y,&m,&n,&L)!=EOF)
{
ll t,p;
ll a1,b1,d1;
d1=x-y;
b1=L;
a1=n-m;
ll k=gcd(a1, b1);
if(d1%k!=0)
{
printf("Impossible\n");
continue;
}
exgcd(a1, b1, t, p);
ll r=b1/gcd(a1, b1);
printf("%lld\n",want(t*d1/k, r));
}
return 0;
}