InputFirst line contains an integer T, which indicates the number of test cases.
Every test case contains two integers exex and eyey, which is the destination grid.
?? 1≤T≤1000
?? 1≤ex,ey≤109.Output For every test case, you should output " Case #x: y", where x indicates the case number and counts from 1 and y is the number of possible starting grids.
Sample Input
3 6 10 6 8 2 8
Sample Output
Case #1: 1 Case #2: 2 Case #3: 3
【题目大意】:一只青蛙在坐标系中跳跃,假设它某一时刻位于(x,y),那么它下一次可以跳到(x+z,y) 或者 (x, y+z),其中Z = LCM(x , y),就是x,y的最小公倍数。现在已知青蛙跳到了(ex,ey)
问青蛙可能的起点有多少个?从这样的起点跳若干次可以到达(ex , ey),可以一次不跳,要跳必须向右跳或者向上跳,步长为z。
【题解】:首先明白这样一个事实,假设能到达终点(ex,ey)的最左下角的起点是(x,y), 那么沿途的所有点(x‘,y‘)都是可以到达终点的点。 设GCD(ex , ey) = k, 由于每次跳跃的步长是原坐标中横坐标x和纵坐标y的LCM,即z,从(x,y)出发,不论新的坐标( x‘ , y‘)是(x+z,y) 还是 (x, y+z),GCD(x‘ , y‘)始终等于k
证明如下:不妨下一步跳到(x+z,y) 设gcd(x,y) = s ; x = ms, y = ns; 显然mn互质
下一步坐标(x‘,y;) gcd(x‘,y‘) = gcd(ms + (ms * ns) / s , ns) = gcd( m*(1+n) , n)
由于m和n互质, n+1 和 n也互质,所以m*(1+n) 和 n必然互质,所以gcd(x‘,y‘) = s所以沿途每一个点的坐标x,y的gcd都相等,等于什么呢,等于最后终点的gcd(ex,ey) = k,这是给定的
【递推】
正推不好推,可以从终点反推,递推公式 f(x , y) = f(x-z1, y) + f(x, y-z2),其中z1 = (x-z1) * y / k 即z1 = x*y / (k+y)
同理 z2 = x*y / (k+x)
【代码】f函数可以写成循环的,若写成递归的,更新x,y坐标的操作一定要写在递归函数的参数里,不能在此之前做,否则会WA,原因是回溯问题
#include<iostream> using namespace std; int gcd(int a, int b){ if(b == 0) return a; else return gcd(b,a%b); } int k; long long ans = 0; void f(long long x, long long y){ if(x == y){ return ; } //其实要往右走x > y是必须的,自己可以稍微证明 ,加一个这个条件可以快一点不加也行 long long z1 = (x*y) / (k+y); //注意保持每一步GCD都是K if( x>y && (x*y) % (k+y) == 0 && gcd(x-z1 , y) == k){ ans++; //cout<<"往右边走 x = "<<x<<" y = "<<y<<endl; f(x - z1,y);
//写成x = x - z1 然后 f(x,y)会WA } long long z2 = (x*y) / (k+x); if(y > x && (x*y) % (k+x) == 0 && gcd(x , y-z2) == k){ ans++; //cout<<"往上边走 x = "<<x<<" y = "<<y<<endl; f(x,y - z2); } } int main(){ int ex,ey; int t; cin>>t; int cas=1; while(t--){ ans = 0; cin>>ex>>ey; k = gcd(ex,ey); f(ex,ey); cout<<"Case #"<<cas++<<": "; cout<<ans+1<<endl; } return 0; }
原文:https://www.cnblogs.com/czsharecode/p/9601109.html