欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【神仙题目】【LCA】洛谷P1852 跳跳棋

程序员文章站 2024-01-08 16:41:10
...

Link

这题妙啊,


题目

【神仙题目】【LCA】洛谷P1852 跳跳棋


由"一次只能跳过一颗棋子",得到此题的一个重要性质:
这三个棋在某一时刻至多有3种跳法:中间的往左右跳,或离中间棋子较近的棋子跳往中间。像这样
【神仙题目】【LCA】洛谷P1852 跳跳棋
而当AB=BC时只有两种跳法(A跳不到A’的位置)
当外面的点往里跳后,这三个棋子的范围会越来越小,直到出现不能往里跳的情况。
我们发现,此时的状态是唯一的:这三个棋子不可能在其它地方重现此时的相对状态。
我们就把这样的状态作为根;
判断两组棋子的根,如果它们不相同,说明它们永远不可能摆成另外一组的样子;直接输出No。
如果它们能摆成同一个根,那么我们就需要LCA(求最近公共祖先)了。
第一组棋子最终会先移动到LCA的状态再移到第二组棋子的状态。

注意
  • 求LCA时我们用不了倍增,但可以用二分加速
  • 万一AB很近而BC很远很远,这里可以优化一下,像这样:
    【神仙题目】【LCA】洛谷P1852 跳跳棋
    AB移动时直接加上 (k-1)*AB 就好了。(平移大法)由于棋子没有特别要区别,所以不用在意AB位置是否交换的问题。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int astep,bstep;
struct asdf{
 int a,b,c;
} A,B,Fka,Fkb;
void px(asdf &LL){   //排序...
 if(LL.a>LL.b) swap(LL.a,LL.b);
 if(LL.a>LL.c) swap(LL.a,LL.c);
 if(LL.b>LL.c) swap(LL.b,LL.c);
}
int getroot(int &aa,int &bb,int &cc){  //找到根
 int step = 0,l,jl1,jl2;
 while(aa+cc!=bb*2){  //如果AB!=AC
  jl1 = bb-aa;//AB
  jl2 = cc-bb;//BC
  if(jl1<jl2){  //左边往里
   l = jl2/jl1;  //计算能跳几次
   if(jl2%jl1 == 0) --l;
   aa += jl1*l;  //平移
   bb += jl1*l;
   step += l;  //统计步数
  } 
  else {  //右边往里 //同上
   l = jl1/jl2;
   if(jl1%jl2 == 0) --l;
   bb -= jl2*l;
   cc -= jl2*l;
   step += l;
  }
 }
 return step;
}
asdf jummp(asdf ll,int step){  //ll状态上跳step步
 int l,jl1,jl2;
 while(step){ 
  jl1 = ll.b-ll.a;  //跟找根的方法相同,不想打注释了
  jl2 = ll.c-ll.b;
  if(jl1<jl2){
   l = jl2/jl1;
   if(jl2%jl1 == 0) --l;
   l = min(l,step);
   ll.a += jl1*l;
   ll.b += jl1*l;
   step -= l;
  } 
  else {
   l = jl1/jl2;
   if(jl1%jl2 == 0) --l;
   l = min(l,step);
   ll.b -= jl2*l;
   ll.c -= jl2*l;
   step -= l;
  }
 }
 return ll;
}
bool db(asdf aa,asdf bb){  //比较这两个状态是否相同
 if(aa.a!=bb.a||aa.b!=bb.b||aa.c!=bb.c) return 0;
 return 1;
}
int main(){
 scanf("%d%d%d%d%d%d",&A.a,&A.b,&A.c,&B.a,&B.b,&B.c);
 px(A); px(B);  //排序
 Fka = A;   //临时储存
 Fkb = B;
 astep = getroot(Fka.a,Fka.b,Fka.c);   //找根
 bstep = getroot(Fkb.a,Fkb.b,Fkb.c);
 if(db(Fka,Fkb)==0){  //根不同
  printf("NO");
  return 0;
 }
 if(astep > bstep) A = jummp(A,astep-bstep);  //先跳到同一高度
 else B = jummp(B,bstep-astep);
 int l = 0,r = min(astep,bstep), mid;  //二分
 while(l<r){
  mid = (l+r)/2;
  Fka = jummp(A,mid);
  Fkb = jummp(B,mid);
  if(db(Fka,Fkb) == 1) r = mid;  //跳mid*2步可以到达
  else l = mid+1;   //不行
 }
 printf("YES\n%d",max(astep-bstep,bstep-astep)+r*2);
}
相关标签: 图论

上一篇:

下一篇: