Description
Analysis
设
S(x,y,z)
为数轴上由左到右依次的三个点,且l=y-x,r=z-y
根据题意,y往两边跳后可以得到
S(x−l,y−l,z)
和
S(x,y+r,z+r)
,而且左右两边只有一个点能往中间跳,那么我们把
S(x,y,z)
看成后两者的父节点,这就是一个树的模型。
那么答案就是树上的起点与终点的距离。
所以我们可以暴力往上跳。但TLE
继续观察,将状态表示变成
(l,r)
,则其可转移到
(l,r+l)
和
(l+r,r)
。
一般地,其可转移到
(l,r+kl)
和
(l+kr,r)
这是不是长得很像辗转相除法里面的两个参数?
这启示我们,一次跳可以跳很多步!大概是log级别的。
通过这样跳可以算深度。
那我们像倍增算法思想一样。但是那个f数组是不能求出来的,所以枚举2^k祖先就真的跳一下看看跳到哪里。
O(log22maxlen)
Code
#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
int x,y,s1,s2,s3,e1,e2,e3;
void jump(int &l,int &r,int k)
{
while(k)
{
if(l==r) break;
if(l>r)
{
if(k<l/r)
{
l=l-k*r;
break;
}
k-=l/r;
if(l%r==0)
{
l=r;
break;
}
else l-=(l/r)*r;
}
else
{
if(k<r/l)
{
r=r-k*l;
break;
}
k-=r/l;
if(r%l==0)
{
r=l;
break;
}
else r-=(r/l)*l;
}
}
}
int dep(int l,int r)
{
int t,k=0;
x=l;
while(1)
{
if(l<r) swap(l,r);
if(l%r==0)
{
x=r,k+=l/r-1;
break;
}
t=r;
k+=l/r,r=l%r,l=t;
}
return k;
}
int lca(int a,int b,int c,int d)
{
int du=dep(a,b),dv=dep(c,d),l,r,x,y,ans=0;
if(du<dv) swap(du,dv),swap(a,c),swap(b,d);
fd(i,log2(du-dv),0)
{
l=a,r=b;
jump(l,r,1<<i);
int dp=dep(l,r);
if(dp>=dv) du=dp,a=l,b=r,ans+=1<<i;
if(dp==dv) break;
}
if(a==c && b==d) return ans;
fd(i,log2(du),0)
{
l=a,r=b,x=c,y=d;
jump(l,r,1<<i);
jump(x,y,1<<i);
if(l==x && r==y) continue;
a=l,b=r,c=x,d=y,ans+=2*(1<<i);
}
return ans+2;
}
int main()
{
scanf("%d %d %d %d %d %d",&s1,&s2,&s3,&e1,&e2,&e3);
if(s1>s2) swap(s1,s2);if(s2>s3) swap(s2,s3);if(s1>s2) swap(s1,s2);
if(e1>e2) swap(e1,e2);if(e2>e3) swap(e2,e3);if(e1>e2) swap(e1,e2);
int a=s2-s1,b=s3-s2,c=e2-e1,d=e3-e2;
dep(a,b),y=x;
dep(c,d);
if(x!=y)
{
printf("NO");
return 0;
}
printf("YES\n%d",lca(a,b,c,d));
return 0;
}