洛谷P1852:跳跳棋(LCA,树形结构)

解析

考虑一个三元组 ( x , y , z ) (x,y,z) (x,y,z),看它能如何跳
要么是 y y y往左右跳,左右边界会变大
要么是两边往中间跳,由于最多跨过一个棋子,所以左右边界会变小
当三点等距时,无法往中间跳
于是我们惊喜的发现:可以互相转移的状态形成了一个二叉树形结构!!!
往中间跳的时候,相当于跳父亲;往两边跳相当于跳儿子

当且仅当两个状态的根相同时,有解
又由于这个跳的操作是可逆的,所以我们的问题就可以转化为两个点在树上的距离
于是我们就可以用类似倍增求LCA的思路解决本题

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e6+100;
const double eps=1e-6;
const int mod=1333331;
inline ll read(){
    ll x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    return f*x;
}

int n,m,p;
struct node{
  ll a,b,c;
  inline void solve(){
    if(b>c) swap(b,c);
    if(a>b) swap(a,b);
    if(b>c) swap(b,c);
    return;
  }
  bool operator == (const node u)const{return a==u.a&&b==u.b&&c==u.c;}
  bool operator != (const node u)const{return a!=u.a||b!=u.b||c!=u.c;}
}u,v;
node walk(node o,ll lft){
  if(lft==0) return o;
  ll d1=o.b-o.a,d2=o.c-o.b;
  if(d1==d2) return o;
  if(d1<d2){
    int k=min((d2-1)/d1,lft);
    o.a+=k*d1,o.b+=k*d1;
    return walk(o,lft-k);
  }
  else{
    int k=min((d1-1)/d2,lft);
    o.b-=k*d2;o.c-=k*d2;
    return walk(o,lft-k);
  }
}
int dep(node o){
  int st=0,ed=2e9;
  while(st<ed){
    int mid=(st+ed)>>1;
    if(walk(o,mid+1)==walk(o,mid)) ed=mid;
    else st=mid+1;
  }
  return st;
}
void print(node o){
  printf("(%lld %lld %lld)\n",o.a,o.b,o.c);return;
}
int main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  u=(node){read(),read(),read()};
  v=(node){read(),read(),read()};
  u.solve(),v.solve();
  if(walk(u,2e9)!=walk(v,2e9)) printf("NO\n");
  else{
    printf("YES\n");
    int aa=dep(u),bb=dep(v);
    if(aa<bb) swap(u,v);
    int num=abs(aa-bb);
    int ans=num;
    for(int k=30;k>=0;k--){
      if(num>=(1<<k)) u=walk(u,1<<k),num-=(1<<k);
    }
    if(u==v){
      printf("%d\n",ans);return 0;
    }
    //printf("-- ans=%d\n",ans);
    //print(u);print(v);
    for(int k=30;k>=0;k--){
      node uu=walk(u,1<<k),vv=walk(v,1<<k);
      if(uu==vv) continue;
      u=uu;v=vv;ans+=(1<<(k+1));
    }
    printf("%d\n",ans+2);
  }
  return 0;
}
/*
  4a
  1 2
  2 3
  1 3
  3 4
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值