题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5584
题目大意:
在一个无限大的棋盘上,若起点为( x , y ) (x,y) ( x , y ) ,则人每次可以选择向上走或者向右走,长度为l c m ( x , y ) lcm(x,y) l c m ( x , y ) , 即走到( x + l c m ( x , y ) , y ) (x+lcm(x,y),y) ( x + l c m ( x , y ) , y ) 或者( x , y + l c m ( x , y ) ) (x,y+lcm(x,y)) ( x , y + l c m ( x , y ) ) ,给定一个终点的坐标,求可以走到此处的合法起点有多少个
分析:
l c m ( x , y ) = x ∗ y g c d ( x , y ) lcm(x,y) = \frac{x*y}{gcd(x,y)}
l c m ( x , y ) = g c d ( x , y ) x ∗ y
由于$x = pgcd(x,y) , y = q gcd(x,y) $ 所以l c m ( x , y ) = p ∗ q ∗ g c d ( x , y ) lcm(x,y) = p*q*gcd(x,y) l c m ( x , y ) = p ∗ q ∗ g c d ( x , y ) ,每次加上一个g c d ( x , y ) gcd(x,y) g c d ( x , y ) 后,两坐标的gcd值不改变
根据此性质,我们可以列出方程,若向右走,则有
{ x + l c m ( x , y ) = a y = b \left\{
\begin{array}{c}
x +lcm(x,y) = a \\
y= b \\
\end{array}
\right.
{ x + l c m ( x , y ) = a y = b
代入可得
\begin{align*}x+x*b/gcd(x,y) =a \Longleftrightarrow
x = \frac{a}{1+\frac{b}{gcd(x,y)}}\end{align*}
由于之前推得g c d ( x , y ) = g c d ( a , b ) gcd(x,y)=gcd(a,b) g c d ( x , y ) = g c d ( a , b )
已知落点为( a , b ) (a,b) ( a , b ) ,可以推得两种之前一步的位置,当g c d ( x , y ) ≠ g c d ( a , b ) gcd(x,y)\not= gcd(a,b) g c d ( x , y ) = g c d ( a , b ) 或者$x<1 或 或 或 y<1$时,即为退出条件,因此直接dfs即可
类似gcd,由于每一次x x x 或者y y y 都会减小为之前的1 1 + b g c d ( x , y ) ≤ 1 2 \frac{1}{1+\frac{b}{gcd(x,y)}}\leq \frac{1}{2} 1 + g c d ( x , y ) b 1 ≤ 2 1 ,所以这至多是一个指数级的递归深度,况且实际上远小于l o g 2 n log_2{n} l o g 2 n ,所以可以放心dfs
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include<bits/stdc++.h> using namespace std; int x,y,g; int dfs(int x,int y) { if (x<=0||y<=0||__gcd(x,y)!=g) return 0; int res = 1; int a = y/g+1 , b = x/g+1; if (x%a==0) res += dfs(x/a,y); if (y%b==0) res += dfs(x,y/b); return res; } int main() { int T,t=1; scanf("%d",&T); while (T--) { scanf("%d%d",&x,&y); g = __gcd(x,y); printf("Case #%d: %d\n",t++,dfs(x,y)); } return 0; }