上链接:1061 -- 青蛙的约会
题意
两只青蛙在循环坐标轴上朝着同一方向跳跃,其中两只青蛙的起始位置和单次跳跃距离给定,以及坐标轴的长度给定,求使得两只青蛙相遇的跳跃次数。
分析
坐标轴,跳跃,模拟!NO!不可能!
数学题嘛...起始位置分别为x和y,单次跳跃距离分别为m,n,一条坐标轴的长度为L,把这条循环的坐标轴看成无限延伸的,长度为L的倍数的坐标轴,很容易想到以下表达式
设青蛙A的起始坐标为x,单次跳跃距离为m,B青蛙起始坐标为y,单次跳跃距离为n,则有
【1】,其中p代表跳跃的次数
可以证明,在两只青蛙跳跃间距出现L的整数倍数时(在无限坐标轴上),在循环坐标轴上即为相遇。
对于上式,x,y,a,b,L皆为已知量,p,q为变量,故可稍化简该式子,得到:
【2】
观察式子,很容易看到这是标准的二元一次方程ax+by=n的形式,若方程【2】存在整数解,青蛙就有可能相遇,同时本题要求的是p的最小整数解。
至于如何判断ax+by=n形式的二元一次方程是否有解,在有解的情况下如何求解,求出解之后如何得到最小正整数解,考虑拓展欧几里得算法。
拓展欧几里得
对于方程二元一次方程ax+by=n,有解的充要条件是gcd(a,b)可以整除n。
稍作解释即:令a=gcd(a,b)a',b=gcd(a,b)b',有ax+by=gcd(a,b)(a'x+b'y)=n,如果x,y,a',b'都是整数,那么c必须是gcd(a,b)的倍数才能使得方程有解。
在有解的情况下,若能够确定该方程的一个特解(x0,y0),那么则能够求得通解,在此不做赘述,对于最小非负整数解则有:
那么如何求这个特解(x0,y0)呢?
引入拓展欧几里得算法
当方程符合ax+by=gcd(a,b)时,可用拓展欧几里得求得特解(x0,y0),代码如下
ll exgcd(ll a,ll b,ll &x,ll &y) {
if(b==0) {
x=1;
y=0;
return a;
}
ans=exgcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
//顺便返回a,b的公约数
}
但这仅求得的是方程ax+by=gcd(a,b)的一个特解,而非我们所要的ax+by=n的特解
由于方程满足gcd(a,b)可以整除n,则可将方程两边共乘n/gcd(a,b),得到以下式子
故可知,若求得方程ax+by=gcd(a,b)的特解为(x0,y0),则对应的ax+by=n方程的特解就在原特解上乘上对应的系数n/gcd(a,b)即可。
随后可根据题目要求套之前的求最小非负整数解的公式。
提示
本题中容易出错的是,保证方程求得非负解,要求a>=0,即n-m>=0,故应当注意在输出之后处理系数。
代码如下
#include<iostream>
using namespace std;
typedef long long ll;
ll ans;
ll exgcd(ll a,ll b,ll &x,ll &y) {
if(b==0) {
x=1;
y=0;
return a;
}
ans=exgcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
}
int main() {
ios::sync_with_stdio(false);
ll x,y,n,m,l;
cin>>x>>y>>m>>n>>l;
ll a=n-m,b=l,c=x-y;
if(a<0) {
a=-a;
c=-c;
}
ll x0,y0;
exgcd(a,b,x0,y0);
ll g=ans;
if(c%g) {
cout<<"Impossible\n";
}else {
ll u=((x0*(c/g))%(b/g)+(b/g))%(b/g);
cout<<u<<endl;
}
}
学习笔记。。没有严谨的证明,套路向