一、题目
二、解法
乍一看,这不是个搜索题吗?直接跑
b
f
s
bfs
bfs,发现只有
20
20
20分
q
w
q
qwq
qwq。
发现每个状态都可以产生三种状态,我们把中间的棋子往两边跳视为当前状态到两个子状态,把两边的棋子往中间跳视为当前状态到一个父状态(至多有一个棋子满足跳的条件),这样原来的搜索就转化成了在一棵二叉树上找两点间的最短路,我们先求出这两个点的深度,然后把他们调整到同一个深度,再一步一步向上跳。
发现时间主要消耗在爬树的过程,考虑对这个过程加速,由于我们现在的跳跃是等价于在长的距离(两点到中间的距离)中减去短的距离,我们自然地想到了用辗转相除法对跳跃进行优化,时间变为
l
o
g
log
log级。由于我们的图还没有建好,调整深度后,我们可以用二分的方式二分向上跳的步数,然后用类似辗转相除法的方式快速跳,这样我们就得到了一个
l
o
g
2
log^2
log2的算法。
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int ans,ad;
struct node
{
int a,b,c,dep;
void get()
{
int x=read(),y=read(),z=read();
a=min(x,min(y,z));
c=max(x,max(y,z));
b=x+y+z-a-c;
}
bool operator != (const node &x) const
{
return (a!=x.a || b!=x.b || c!=x.c);
}
} s,e;
int Abs(int x)
{
return x>0?x:-x;
}
node find_rt(node x,int &dep)
{
int d1=x.b-x.a,d2=x.c-x.b;
if(d1<d2)
{
int tmp=d2/d1,d=d2%d1;
if(d==0)
{
d=d1;
tmp--;
}
dep+=tmp;
return find_rt(node{x.c-d-d1,x.c-d,x.c,0},dep);
}
if(d1>d2)
{
int tmp=d1/d2,d=d1%d2;
if(d==0)
{
d=d2;
tmp--;
}
dep+=tmp;
return find_rt(node{x.a,x.a+d,x.a+d+d2,0},dep);
}
return x;
}
node search(node x,int step)
{
int d1=x.b-x.a,d2=x.c-x.b;
if(d1<d2)
{
int tmp=d2/d1,d=d2%d1;
if(d==0)
{
d=d1;
tmp--;
}
if(step<tmp)
return node{x.a+step*d1,x.a+(step+1)*d1,x.c,0};
return search(node{x.c-d-d1,x.c-d,x.c,0},step-tmp);
}
if(d1>d2)
{
int tmp=d1/d2,d=d1%d2;
if(d==0)
{
d=d2;
tmp--;
}
if(step<tmp)
return node{x.a,x.c-(step+1)*d2,x.c-step*d2,0};
return search(node{x.a,x.a+d,x.a+d+d2,0},step-tmp);
}
return x;
}
void conquer(int l,int r)
{
if(l>r) return ;
int mid=(l+r)/2;
if(search(s,mid)!=search(e,mid))
conquer(mid+1,r);
else
{
ans=mid;
conquer(l,mid-1);
}
}
signed main()
{
s.get();
e.get();
if(find_rt(s,s.dep)!=find_rt(e,e.dep))
return !puts("NO");
puts("YES");
ad=Abs(s.dep-e.dep);
if(s.dep>e.dep)
s=search(s,ad);
else
e=search(e,ad);
conquer(0,0x3f3f3f3f);
printf("%lld\n",2*ans+ad);
}