P1852 [国家集训队]跳跳棋
lca+二分
详细解析见题解
对于每组跳棋,我们可以用一个三元组(x,y,z)表示
我们发现,这个三元组的转移具有唯一性,收束性
也就是说,把每个三元组当成点,以转移关系为边,那么可以得到一棵树
显然最短步数==lca
然后我们就可以愉快地跑lca了
但是还要加优化,就是有可能出现2个靠得近的棋子,但与另一个棋子离得远的情况
这时要跳很多次,但是可以加速,详见代码
最后二分求lca
code:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cctype> using namespace std; struct node{ int a[3]; bool operator == (const node &tmp) const{return a[0]==tmp.a[0]&&a[1]==tmp.a[1]&&a[2]==tmp.a[2];} }f,t,p1,p2; inline int find(node x){ int d1=x.a[1]-x.a[0],d2=x.a[2]-x.a[1]; bool c=0; if(d1==d2) {p1=x; return 0;} if(d1<d2) swap(d1,d2),c=1; int cnt=d1/d2,d=d1%d2; //加速跳 if(!d) d+=d2,--cnt; if(c) cnt+=find((node){x.a[2]-d-d2,x.a[2]-d,x.a[2]}); else cnt+=find((node){x.a[0],x.a[0]+d,x.a[0]+d+d2}); return cnt; } inline void change(node x,int step){ int d1=x.a[1]-x.a[0],d2=x.a[2]-x.a[1]; bool c=0; if(d1==d2||!step) {p1=x; return ;} if(d1<d2) swap(d1,d2),c=1; int cnt=d1/d2,d=d1%d2; if(!d) d+=d2,--cnt; if(c){ if(step>=cnt) change((node){x.a[2]-d-d2,x.a[2]-d,x.a[2]},step-cnt); else change((node){x.a[2]-d-d2*(cnt-step+1),x.a[2]-d-d2*(cnt-step),x.a[2]},0); } else{ if(step>=cnt) change((node){x.a[0],x.a[0]+d,x.a[0]+d+d2},step-cnt); else change((node){x.a[0],x.a[0]+d+d2*(cnt-step),x.a[0]+d+d2*(cnt-step+1)},0); } } inline bool same(int k){ change(f,k); node r1=p1; change(t,k); node r2=p1; return r1==r2; } int main(){ scanf("%d%d%d%d%d%d",&f.a[0],&f.a[1],&f.a[2],&t.a[0],&t.a[1],&t.a[2]); sort(f.a,f.a+3); sort(t.a,t.a+3); int sp1=find(f); p2=p1; int sp2=find(t); //求相对于树根的深度 if(!(p1==p2)) {printf("NO"); return 0;} //树根不同 if(sp1<sp2) swap(sp1,sp2),swap(f,t); int ans=sp1-sp2; change(f,ans); f=p1; //使两点同一深度 int l=0,r=sp2; //二分找lca while(l<r){ int mid=l+((r-l)>>1); if(same(mid)) r=mid; else l=mid+1; }printf("YES\n%d",(l<<1)+ans); return 0; }