[洛谷P1852][题解]跳跳棋

作为紫题却是一道巨大神的人类智慧题,由于智商不足所以看了题解……
观察跳棋的性质,我们发现中间的棋子可以通过向两边跳扩张区间,两边的棋子最多有一个能通过向中间跳缩小区间,并且最后会缩成两边的棋子关于中间对称的形态。
先解决判定无解的问题。由于一个区间向外可以扩张成两个,向内可以缩小成一个,我们利用缩小的唯一性,看两个状态不断缩小时是否有交集即可。
然后最优解可以直接加一个二分。
但是这样你发现会 T 飞,我们不能每次只跳一步。发现有些步数可以用取模优化掉,最终复杂度变成了辗转相除一样的东西,可以通过此题。

如果你还是理解不了上面的过程,这里给出另一种理解方式:把扩张想象成儿子,缩小想象成父亲,这样就是一棵二叉树,答案就是树上路径的距离。

int ans;
struct Tri {
  int a,b,c;
  void S(){
    if(a>b)swap(a,b);
    if(a>c)swap(a,c);
    if(b>c)swap(b,c);
  }
}A,B;
bool operator == (Tri A,Tri B){
  return A.a==B.a&&A.b==B.b&&A.c==B.c;
}
il Tri Get(Tri _A,int &stp){//get final status
  Tri A=_A;A.S();
  while(A.b-A.a!=A.c-A.b){
    int lt=A.b-A.a,rt=A.c-A.b;
    if(lt<rt){//a jumps over b
      int q=rt/lt,r=rt%lt;
      if(!r)r=lt,q--;
      A.a+=rt-r,A.b+=rt-r,stp+=q;
    }else {//c jumps over b
      int q=lt/rt,r=lt%rt;
      if(!r)r=rt,q--;
      A.c-=lt-r,A.b-=lt-r,stp+=q;
    }
  }
  return A;
}
il Tri Check(Tri _A,int lmt){
  Tri A=_A;A.S();
  while(lmt>0){
    int lt=A.b-A.a,rt=A.c-A.b;
    if(lt<rt){//a jumps over b
      int q=rt/lt,r=rt%lt;
      if(!r)r=lt,q--;
      if(q>lmt)q=lmt;
      A.a+=q*lt,A.b+=q*lt,lmt-=q;
    }else {//c jumps over b
      int q=lt/rt,r=lt%rt;
      if(!r)r=rt,q--;
      if(q>lmt)q=lmt;
      A.c-=q*rt,A.b-=q*rt,lmt-=q;
    }
  }
  return A;
}
int main(){
  Read(A.a),Read(A.b),Read(A.c);
  Read(B.a),Read(B.b),Read(B.c);
  A.S(),B.S();
  int stpa=0,stpb=0;
  if(!(Get(A,stpa)==Get(B,stpb)))return puts("NO"),0;
  int d;
  if(stpa<stpb)d=stpb-stpa,B=Check(B,d);
  else d=stpa-stpb,A=Check(A,d);
  int l=0,r=min(stpa,stpb);
  while(l<=r){
    int m=nmid;Tri _A=Check(A,m),_B=Check(B,m);
    if(_A==_B)ans=m,r=m-1;
    else l=m+1;
  }
  cout<<"YES"<<endl<<ans+ans+d<<endl;
  KafuuChino HotoKokoa
}
内容来自_ajhfff_的博客(https://www.cnblogs.com/juruoajh/),未经允许,不得转载。
原文地址:https://www.cnblogs.com/juruoajh/p/14963173.html