BZOJ 2144: 跳跳棋

16 篇文章 1 订阅
10 篇文章 0 订阅

这道题太神了 像我这种蒟蒻的脑瓜哪里能想出来QAQ….

我们可以发现其实最多只有3种跳的方法 中间往外跳2种 左右往中间跳 1种或没有(等距的时候就没有了)
其实左右往中间跳之后 又能以往外跳跳到原状态 其实就是一个二叉树 往中间跳就是父亲状态 往外跳就是左右孩子状态
所以看看初始状态和末状态是否在一颗树上就能判断有解还是无解

关于怎么跳 可以用一种类似gcd的方法(复杂度log)
令a=y-x,b=z-y 若 a< b可以直接跳到 a,b-a
反之同理 以此类推 (a,b)->(a%b,b)或(a,b%a) GCD!
最后我用了二分的方法确定lca。

代码借鉴了一下别人的 感觉很简洁清晰明了啊~

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
inline int read()
{
    char ch=getchar(); int x=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0'; ch=getchar();}
    return x*f;
}
struct node{
    int x,y,z;
    void init(){
        x=read(),y=read(),z=read();
        if(x>y)swap(x,y); if(x>z)swap(x,z); if(y>z)swap(y,z);
    }
    bool operator==(node o)const{return x==o.x&&y==o.y&&z==o.z;}
}a,b,p,q;
int len,k;
node getfa(node t,int dep){
    for(len=0;dep;len+=k){
        int u=t.y-t.x,v=t.z-t.y;
        if(u==v)return t;
        if(u<v){
            k=min((v-1)/u,dep);
            t.x+=k*u,t.y+=k*u,dep-=k;
        }
        else{
            k=min((u-1)/v,dep);
            t.y-=k*v,t.z-=k*v,dep-=k;
        }
    }
    return t;
}
int main()
{
    a.init(),b.init(); int l1,l2;
    p=getfa(a,inf),l1=len,q=getfa(b,inf),l2=len;
    if(p==q); else {printf("NO\n"); return 0;}
    if(l1<l2)swap(a,b),swap(l1,l2);
    a=getfa(a,l1-l2);
    int l=0,r=l2,mid,ans;
    while(l<=r){
        mid=(l+r)>>1;
        if(getfa(a,mid)==getfa(b,mid))ans=mid,r=mid-1; else l=mid+1;
    }
    printf("YES\n%d\n",(ans<<1)+l1-l2);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值