#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43235053"); }
题意:首先一个状态至多有3种跳的方法的~不能隔着格子跳的~
题解:
既然有三种方法,那么显然有两种是向外跳,一种是收敛着跳(往里)
然后这个就可以类比成父亲状态和子状态,
里兮为父,外则即子。(诶窝里斗的感觉,,这文言文有点喜感)
然后我们就发现步数是开始状态和结束状态都往里走,走到lca的步数。
或者说开始状态走到lca,然后再由lca走到结束状态。。
代码跟思路是一样的:(有详细注释~)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 5 #define inf 0x3f3f3f3f using namespace std; struct Status { int x[N]; void read(){for(int i=1;i<=3;i++)scanf("%d",&x[i]);} bool operator != (const Status &a)const {return x[1]!=a.x[1]||x[2]!=a.x[2]||x[2]!=a.x[2];} }S,T; Status cal(const Status &a,int k,int &ret) // 跳k次 { Status ans=a; int i,j,dis1=a.x[2]-a.x[1],dis2=a.x[3]-a.x[2]; if(dis1==dis2)return ans; else if(dis1<dis2) { int t=min(k,(dis2-1)/dis1); // 往父亲状态跳跳跳跳。 // t是需要跳的次数(跳到头) // -1 是避免跳重合了 k-=t,ret+=t; ans.x[2]+=t*dis1,ans.x[1]+=t*dis1; // 第一枚棋子未必还是第一枚 // 第二枚棋子也未必还是第二枚了。 // 算是又排好序了吧(画一下t是奇数的情况,比如t==1) } else // 同上,情况讨论而已 { int t=min(k,(dis1-1)/dis2); k-=t,ret+=t; ans.x[2]-=t*dis2,ans.x[3]-=t*dis2; } if(k)return cal(ans,k,ret); else return ans; } int dis1,dis2,ans; int main() { // freopen("test.in","r",stdin); int i,j,k,temp=0; S.read(),T.read(); sort(S.x+1,S.x+4); sort(T.x+1,T.x+4); int reta=0,retb=0; Status final_s=cal(S,inf,reta),final_t=cal(T,inf,retb); if(final_s!=final_t){puts("NO");return 0;} // 棋子方案可以构造成森林~ // 所以需要两个状态是一个父亲~~ else { puts("YES"); if(reta>retb) { swap(reta,retb); swap(S,T); }// 我们让T更深! ans=retb-reta; T=cal(T,ans,temp); // 模拟倍增LCA的过程 // 我们会先让两者深度相同 int l=0,r=reta,mid; while(l<=r) // 诶一样的。 { mid=l+r>>1; if(cal(S,mid,temp)!=cal(T,mid,temp))l=mid+1; else r=mid-1; } printf("%d\n",ans+2*l); // 肯定得先到lca再变换的 // 不会有更优解了~~~ } return 0; }
原文地址:http://blog.csdn.net/vmurder/article/details/43235053