Description
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。
Input
Output
Sample Input
1 2 3 4 5
Sample Output
4
题意:大体就是A在x处开始跳,每次跳m,b在y处开始,每次跳n问两个能否相遇,路径总长度为L,是循环的一个环。
思路:数论题,用到的是扩展欧几里得,好吧是我太菜了一直没搞懂这个算法。
首先可以得出一个方程[x+k*m](mod L)≡[y+k*n](mod L),可以转化为(n-m) * k ≡ x-y (mod L),
进而得到(m-n)*k+s*L=y-x(好吧这个转换不会,从大佬那抄来的),
现在已经符合ax+by=c的方程了,设a=m-n,b=L,c=y-x,然后套用模板求出特解k的值,注意k>0,所以要用通解公式得出最小正整数。
用到三个定理:
1 gcd(a,b)是ax+by的线性组合的最小正整数,x,y∈z;如果d = gcd(a, b),则必能找到正的或负的整数k和l,使d = a*k + b*l
2 如果ax+by=c,x,y∈z;则c%gcd(a,b)==0;若gcd(a, b)=1,则方程ax ≡ c (mod b)在[0, b-1]上有唯一解。
3 如果是互质的正整数,是整数,且方程ax+by=c,有一组整数解x0,y0则此方程的一切整数解可以表示为x=x0+bt;y=y0-at;t∈z;
用扩展的欧几里德求出其中一组解t0 ,p0, 并令d = gcd(a, b),有 a * t0 + b * p0 = d; (2)
(2)式两边都乘(c/ d) 得 a * t0 *(c / d) + b * p0 * (c / d) = d* (c/d)=d;
因为d = gcd(a, b), 所以 a * t0/ d是整数,b * p0 / d 也是整数,所以 c/ d 也需要是整数,否则无解。
所以t0 * (c / d)是最小的解,但有可能是负数。
因为a * ( t0 *(c / d) + b*n) + b * (p0 * (c / d) – a*n) = c; (n是自然数),所以解为 (t0 * (c / d) % b + b)%b。
关于扩展欧几里得:
对于不完全为0的非负整数a, b gcd(a, b)表示a, b 的最大公约数。那么存在整数x, y使得 gcd(a, b) = a * x + b * y;
不妨设a > b
①,当b = 0 时,gcd(a, b) = a , 此时 x = 1, y = 0;
②,当 a * b 大于或小于 0 时,
设 a * x + b * y = gcd(a, b); (1)
b * x0 + (a % b) * y0 = gcd( b, a % b); (2)
由欧几里德公式: gcd(a, b) = gcd (b, a % b);得(1),(2)
a * x + b * y = b * x0 + (a % b) * y0
= b * x0 + (a – a / b * b) * y0
= a * y0 + ( x0 – a / b * y0 ) * b
所以 x = y0, y = x0 – a / b * y0;
由此可以得出扩展欧几里德算法。
代码:
1 #include <map> 2 #include <set> 3 #include <list> 4 #include <stack> 5 #include <queue> 6 #include <deque> 7 #include <cmath> 8 #include <ctime> 9 #include <string> 10 #include <limits> 11 #include <cstdio> 12 #include <vector> 13 #include <iomanip> 14 #include <cstdlib> 15 #include <cstring> 16 #include <istream> 17 #include <iostream> 18 #include <algorithm> 19 #define ci cin 20 #define co cout 21 #define el endl 22 #define Scc(c) scanf("%c",&c) 23 #define Scs(s) scanf("%s",s) 24 #define Sci(x) scanf("%d",&x) 25 #define Sci2(x, y) scanf("%d%d",&x,&y) 26 #define Sci3(x, y, z) scanf("%d%d%d",&x,&y,&z) 27 #define Scl(x) scanf("%I64d",&x) 28 #define Scl2(x, y) scanf("%I64d%I64d",&x,&y) 29 #define Scl3(x, y, z) scanf("%I64d%I64d%I64d",&x,&y,&z) 30 #define Pri(x) printf("%d ",x) 31 #define Prl(x) printf("%I64d ",x) 32 #define Prc(c) printf("%c ",c) 33 #define Prs(s) printf("%s ",s) 34 #define For(i,x,y) for(int i=x;i<y;i++) 35 #define For_(i,x,y) for(int i=x;i<=y;i++) 36 #define FFor(i,x,y) for(int i=x;i>y;i--) 37 #define FFor_(i,x,y) for(int i=x;i>=y;i--) 38 #define Mem(f, x) memset(f,x,sizeof(f)) 39 #define LL long long 40 #define ULL unsigned long long 41 #define MAXSIZE 100005 42 #define INF 0x3f3f3f3f 43 44 const int mod=1e9+7; 45 const double PI = acos(-1.0); 46 47 using namespace std; 48 49 LL exgcd(LL a,LL b,LL &x,LL &y) 50 { 51 int d=a; 52 if(b!=0) 53 { 54 d=exgcd(b,a%b,y,x); 55 y-=(a/b)*x; 56 } 57 else 58 { 59 x=1; 60 y=0; 61 } 62 return d; 63 } 64 //函数的返回值是a、b的最大公约数 65 //传进来x、y的引用,进而得到x、y的值 66 67 int main() 68 { 69 LL n,m,x,y,l; 70 cin>>x>>y>>m>>n>>l; 71 LL a,b,c; 72 a=n-m;; 73 b=l; 74 c=x-y; 75 LL r=exgcd(a,b,x,y);//这个扩展欧几里得函数求得是a,b的最大公约数以及ax+by=gcd(a,b)的解x、y 76 if(c%r!=0) 77 Prs("Impossible"); 78 else 79 { 80 x=x*c/r;//得到方程 (n-m)k+l*s=x-y的解 81 //因为解可能有负数,所以判断一下 82 if(x>=0) 83 x%=b/r; 84 else 85 x=(x%b/r+b/r); 86 co<<x; 87 } 88 //cout<<exgcd(12,23,x,y); 89 return 0; 90 } 91 /* 92 关于解的大小问题: 93 ax + by = c 的通解公式 94 x=x1+k*(b/gcd(a,b)) 95 y=y1-k*(a/gcd(a,b)) k为任意整数 96 注意x与y是一个增加一个减少的。 97 98 所以在本题中在x大于0的时候我们应该进行取模,防止两个青蛙周期跳 99 当x小于0的时候就加上b/r取模,但x可能是一个很大的负数,所以可以先取模在加上b/r保证得到正数 100 */