1. 程式人生 > >P1852 [國家集訓隊]跳跳棋

P1852 [國家集訓隊]跳跳棋

www 二分 bsp amp hang ostream 國家 code pre

P1852 [國家集訓隊]跳跳棋

lca

詳細解析見題解

對於每組跳棋,我們可以用一個三元組(x,y,z)表示

我們發現,這個三元組的轉移具有唯一性,收束性

也就是說,把每個三元組當成點,以轉移關系為邊,那麽可以得到一棵樹

顯然最短步數==lca

然後我們就可以愉快地跑lca了

但是還要加優化,就是有可能出現2個靠得近的棋子,但與另一個棋子離得遠的情況

這時要跳很多次,但是可以加速,詳見代碼

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; }

P1852 [國家集訓隊]跳跳棋