bzoj 2144: 跳跳棋

顶级大神题QAQ
完全没思路了这次。。

然后就去膜了题解。。感觉我很诚实啊
感觉真是神奇啊。。

我们先把棋子排序,这样好搞一点
首先,题目有要求,不可以跳过别人
那么这也就是说,两边的棋子只有一个是可以动的,但是中间向左向右跳都可以
当然,也有左右都不能动的情况
所以一个点就有三种扩展方式啦!
然后动的方式是可逆的,这个没有问题。。
于是问题可以想象为一棵树,向上跳是两旁的跳,然后儿子就是中间的跳
于是问题就转化为求树上两个点的距离
但是这题不是一颗树,是一个森林。。这个大家都知道吧,所有有NO的情况。。
然后求他的父亲的时候,要一次跳多个,要不太慢

就这样吧

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int MAX=1<<30;
struct qq
{
    int x[5];
    void read () {for (int u=1;u<=3;u++) scanf("%d",&x[u]);}
    void Sort () {sort(x+1,x+4);}
    bool operator != (const qq &a)const
    {return x[1]!=a.x[1]||x[2]!=a.x[2]||x[3]!=a.x[3];};
}S,T;
int mymin (int x,int y){return x<y?x:y;}
qq cal (qq a,int k,int &ret)
{
    qq ans=a;
    int dis1=a.x[2]-a.x[1],dis2=a.x[3]-a.x[2];
/*  for (int u=1;u<=3;u++) printf("%d ",a.x[u]);

    printf("\n");*/
    if (dis1==dis2)//到这一棵树的根了
        return ans;
    else if (dis1<dis2)//左边跳
    {
        int t=mymin(k,(dis2-1)/dis1);// -1 是避免跳重合了 
        k-=t;
        ret+=t;
        ans.x[2]+=t*dis1;
        ans.x[1]+=t*dis1;
    }
    else
    {
        int t=mymin(k,(dis1-1)/dis2);
        k-=t;
        ret+=t;
        ans.x[3]-=t*dis2;
        ans.x[2]-=t*dis2;
    }
    if (k!=0) return cal(ans,k,ret);
    return ans;
}
int main()
{
    S.read();S.Sort();
    T.read();T.Sort();
    int lalal1=0,lalal2=0;
    qq a=cal(S,MAX,lalal1),b=cal(T,MAX,lalal2);//这两个节点的深度 
//  printf("%d %d\n",lalal1,lalal2);
    if (a!=b) {printf("NO");return 0;}//这两个点不在一棵树上,不可能有解
    else
    {
        printf("YES\n");
        if (lalal1>lalal2)
        {
            swap(lalal1,lalal2);
            swap(S,T);
        }//让T更深
        int ans=lalal2-lalal1;//先跳到同一深度
        int A;
        T=cal(T,ans,A);
        int l=0,r=lalal1,ans1;
        while (l<=r)
        {
            int mid=(l+r)>>1;
            if (cal(S,mid,A)!=cal(T,mid,A)) l=mid+1;
            else {r=mid-1;ans1=mid;}
         } 
         printf("%d\n",ans+ans1*2);
    } 
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值