[bzoj2144][LCA]跳跳棋

55 篇文章 0 订阅

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

HINT

【范围】

100% 绝对值不超过10^9

题解

神题..
很容易发现一个三元组只有三种跳法
b往左边/右边跳 或者距离b较近的那个往里跳
如果a,c距离b的距离一样,那么只有两种跳法
下来就很神奇了!
我们把距离b较近那个往里跳转移到的状态设为这个状态的父亲节点
另外两个转移设为孩子节点
a,c距离b的距离一样的作为根
发现这可以构成一棵树!!!
那不就是求树上两点之间路径和嘛..
首先判一下两个状态是否在同一个根下,不是输出NO
考虑模拟一个倍增的过程
设b距离a为n1 c距离b为n2 不妨仅讨论n1 < n2的情况
此时可以向右跳动(n2-1)/n1次
先将两个点移到相同深度,然后二分答案判断即可..

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct node
{
    int a,b,c;
    node(){}
    node(int _a,int _b,int _c){a=_a;b=_b;c=_c;}
}g1,g2;
int sum;
void jump(node &g,int k)
{
    int n1=g.b-g.a,n2=g.c-g.b;
    if(!k)return ;
    if(n1==n2)return ;
    if(n1<n2)
    {
        int p=min(k,(n2-1)/n1);k-=p;sum+=p;
        g.b=g.b+n1*p;g.a=g.b-n1;
    }
    else
    {
        int p=min(k,(n1-1)/n2);k-=p;sum+=p;
        g.b=g.b-n2*p;g.c=g.b+n2;
    }
    if(!k)return ;
    jump(g,k);
}
bool chk(node g1,node g2)
{
    if(g1.a!=g2.a||g1.b!=g2.b||g1.c!=g2.c)return false;
    return true;
}
bool check(int p)
{
    node p1=g1,p2=g2;
    jump(p1,p);jump(p2,p);
    return chk(p1,p2);
}
int u[4];
int main()
{
    node u1,u2;
    scanf("%d%d%d",&u[1],&u[2],&u[3]);sort(u+1,u+1+3);
    u1=g1=node(u[1],u[2],u[3]);
    scanf("%d%d%d",&u[1],&u[2],&u[3]);sort(u+1,u+1+3);
    u2=g2=node(u[1],u[2],u[3]);
    sum=0;jump(u1,(1<<31-1));int s1=sum;
    sum=0;jump(u2,(1<<31-1));int s2=sum;
    if(!chk(u1,u2)){printf("NO\n");return 0;}
    if(s1>s2)swap(g1,g2),swap(s1,s2);
    jump(g2,s2-s1);
    int l=0,r=s1,ans;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    puts("YES");
    printf("%d\n",s2-s1+2*ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值