思路:设青蛙跳了k次,那么就有(x+mk)-(y+nk)=p*L.
即x-y+(m-n)k=p*L,即(m-n)*k≡(y-x) (mod L).这个线性同余方程有解当且仅当gcd(m-n,L)|(y-x).
令a=m-n,b=L,c=y-x.用扩展欧几里得解方程ax+by=c.可以求出原方程的一个解.如何求最小正整数解呢?假设我们已经得到一个x0,令d=gcd(m-n,L),那么所有解可以表示为x=x0+k*L/d.设L'=L/d.Xmin=(x0 mod L'+L') mod L'.
推导:若最终要求的方程为:
ax + by = c = k*gcd(a, b)
并设方程
ax1 + by1 = gcd(a, b) 已有解(x1, y1)
两边同时乘上k:
akx1 + bky1 = k*gcd(a, b)
左边作恒等变换
akx1 + bky1 = k*gcd(a, b) <=>
akx1 + bky1 + k1*lcm(a, b) - k1*lcm(a, b) = k*gcd(a, b) <=>
a(kx1 + k1b/gcd(a, b)) + b(ky1 - k1a/gcd(a, b)) = c ...(5)
其中k1为任意整数,lcm为最小公倍数
比较(4) (5)方程可得
x = kx1 + k1b/gcd(a, b)
y = ky1 - k1a/gcd(a, b)
即为最终解。
扩展欧几里得算法:http://www.acmerblog.com/extend-gcd-5610.html
#include <cstdio> #include <cstring> #define ll long long using namespace std; ll extend_gcd(ll a, ll b, ll &x, ll &y) { if(b == 0) { x = 1, y = 0; return a; } else { ll r = extend_gcd(b, a%b, y, x); y -= x*(a/b); return r; } } int main() { ll x, y, m, n, l; while(~scanf("%I64d%I64d%I64d%I64d%I64d", &x, &y, &m, &n, &l)) { ll t, p; ll ans = extend_gcd(n-m, l, t, p); if((x-y) % ans) printf("Impossible\n"); else { //求最小整数解的算法 t = (x-y)*t/ans; //首先令x为一个特解 t = (t % (l/ans)+(l/ans)) % (l/ans); //再根据公式计算 printf("%I64d\n", t); } } }