bzoj 2144: 跳跳棋——倍增/二分
Posted 友人A
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2144: 跳跳棋——倍增/二分相关的知识,希望对你有一定的参考价值。
Description
跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。 写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。
Input
第一行包含三个整数,表示当前棋子的位置a b c。(互不相同)第二行包含三个整数,表示目标位置x y z。(互不相同)
Output
如果无解,输出一行NO。如果可以到达,第一行输出YES,第二行输出最少步数。
Sample Input
1 2 3
0 3 5
0 3 5
Sample Output
YES
2
【范围】
100% 绝对值不超过10^9
2
【范围】
100% 绝对值不超过10^9
——————————————————————————————————
这道题我们发现如果从中间往两边跳的话 有两种状态 而从两边往中间跳的话只有一种状态
刚好非常符合树形状 那么我么把一个点向外跳的状态在状态树上表示为这个点的儿子
向内表示为父亲 那么如果这两个初始状态在树上有lca就有答案 这个我们可以先像倍增求lca一样
先将两个状态跳到同一深度然后再二分深度(答案)及可以辣
#include<cstdio> #include<cstring> #include<algorithm> using std::swap; using std::min; const int inf=0x3f3f3f3f; int read(){ int ans=0,f=1,c=getchar(); while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();} return ans*f; } int len,lena,lenb,h; struct pos{int x,y,z;}a,b,yy,ly; pos up(pos s,int T){ for(len=0;T;len+=h){ int l=s.y-s.x,r=s.z-s.y; if(l==r) return s; if(l<r) h=min(T,(r-1)/l),s.x+=h*l,s.y+=h*l; else h=min(T,(l-1)/r),s.y-=h*r,s.z-=h*r; T-=h; } return s; } void sort(pos &s){ if(s.x>s.z) swap(s.x,s.z); if(s.x>s.y) swap(s.x,s.y); if(s.y>s.z) swap(s.y,s.z); } int main(){ a.x=read(); a.y=read(); a.z=read(); sort(a); b.x=read(); b.y=read(); b.z=read(); sort(b); yy=up(a,inf); lena=len; ly=up(b,inf); lenb=len; if(yy.x!=ly.x||yy.y!=ly.y||yy.z!=ly.z) return puts("NO"),0; puts("YES"); if(lena<lenb) swap(a,b),swap(lena,lenb); a=up(a,lena-lenb); int l=0,r=lenb; while(l<r){ int mid=(l+r)>>1; yy=up(a,mid); ly=up(b,mid); if(yy.x==ly.x&&yy.y==ly.y&&yy.z==ly.z) r=mid; else l=mid+1; } printf("%d",(l<<1)+lena-lenb); return 0; }
以上是关于bzoj 2144: 跳跳棋——倍增/二分的主要内容,如果未能解决你的问题,请参考以下文章