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

Sample Output

YES
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;
}
View Code
原文地址:https://www.cnblogs.com/lyzuikeai/p/7636496.html