传送门:
http://acm.hdu.edu.cn/showproblem.php?pid=5584
这是本年度上海区域赛第三水的题,数论题,可以用公式做,也可以枚举因子搜索去做,其实本质上就是关键性的一步有没有推导出来!!
二维点,(a,b)可以走到其中任何一个加上二者的最小公倍数,然后给出终点坐标,问可能的起点有多少个?
很显然是要倒推回去,又很容易发现最小公倍数一定比二者都大,那么也就是说当前坐标小的那个值肯定是不动的,所以说逆推的时候点是唯一的,就不存在什么去重之类的问题。
那么,a与b的积除gcd(a,b)即为lcm,很朴素的一个想法就是枚举gcd的值,然后去判断是不是合法,如果此时我们发现这条性质就可以直接求解了,就是路径上所有点的最大公约数都是相等的
证明:利用辗转相除法,则正好可以回退到上一步(简直机智的我)
然后就可以自然而然地推导出上一个位置的坐标了
假设(x,y)(x<y) 那么利用上一个点列一个等式就是(x*a)==gcd(x,y)*(y-a)
a=gcd(x,y)*y/gcd(x,y)+x;
终止条件显然就是x==y,因为此时计算出来的就是小于1的数了
还需要注意的就是计算出来值后一定要判断一下是不是实际的最小公倍数!
改样例的时候发现的!
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;ll x,y;
ll gcd(ll a,ll b){
if(a<b) swap(a,b);
return a%b==0?b:gcd(a%b,b);
}
int main(){
cin>>t;int count=1;
while(t--){
scanf("%lld%lld",&x,&y);int sum=1;
while(x!=y){
if(x>y) swap(x,y);
ll gcder=gcd(x,y);ll pre=y;
if(gcder*y%(gcder+x)!=0) break;
y=gcder*y/(gcder+x);
if((pre-y)%x!=0) break;
sum++;
}
printf("Case #%d: %d\n",count++,sum);
}
}
这份代码写的简直是残,刚开始是最大公因数判断错了,仅仅用了都能整除取最大去判断,那么这样肯定就错了,导致最后的答案会多出来,还调试了半天,就应该直接用gcd去判断嘛,还有最大公因数初值的判断,刚开始以为需要是1,后来发现是0才对,说实话我随意改了一下这处,居然就过了,也是挺吃惊的!!!
后来一想,互质根本就不是终止条件啊
如:6 7, 6 1, 1 3
所以肯定会导致漏解的!
刚开始就是某个位置写残,然后被样例带跑偏了,不过一下子就想到并且改出了这个bug还是令我很开心的!!!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;ll x,y;
int sum;
ll gcd(ll a,ll b){
if(a<b) swap(a,b);
return a%b==0?b:gcd(a%b,b);
}
void check(ll i,ll j){
if((i==100000000)&&(x==100000000)) puts("bingo");
cout<<"tmp="<<j<<endl;
}
void dfs(ll x,ll y){
sum++;
if(x>y) swap(x,y); ll tmp;ll j;ll maxx=0;
for(ll i=1;i<=sqrt(x);i++){
if(x%i==0){
if((i*y%(x+i))==0){ tmp=i*y/(x+i);
if(gcd(x,tmp)==i){maxx=max(maxx,i); }
}
if(i*i!=x){j=x/i;
if((j*y%(x+j))==0){tmp=j*y/(x+j);
if(gcd(tmp,x)==j){maxx=max(maxx,j);}
}
}
}
}ll tt=0;
if(maxx!=0)
{tt=(y*maxx)/(maxx+x);dfs(x,tt);}
}
int main(){
cin>>t;int count=1;
while(t--){
scanf("%lld%lld",&x,&y);sum=0;
dfs(x,y);
printf("Case #%d: %d\n",count++,sum);
}
}