洛谷P1852 跳跳棋

将三颗棋子看作三元组 ((x,y,z) x<y<z),得其能转移到的状态有:

[largeegin{aligned} &(2x-y,x,z) \ &(x,z,2z-y) \ &(x,2y-z,y) (y-x>z-y) \ &(y,2y-x,z) (y-x<z-y) \ end{aligned} ]

后两个转移只会满足其中一个,那么将中间棋子向两边跳看作左右儿子,两边棋子向中间跳看作父亲,根节点为满足 (y-x=z-y) 的状态,那么每个状态都唯一对应一棵二叉树。

到树上的 (lca) 即为最小步数,可以通过除来加快跳父亲的过程,然后二分找 (lca) 即可。

#include<bits/stdc++.h>
#define inf 1000000000
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int dep;
struct node
{
    int x,y,z;
    void init()
    {
        read(x),read(y),read(z);
        int a[3]={x,y,z};
        sort(a,a+3),x=a[0],y=a[1],z=a[2];
    }
}s,t;
bool operator == (const node &a,const node &b)
{
    return a.x==b.x&&a.y==b.y&&a.z==b.z;
}
node find(node p,int d,int goal)
{
    int x=p.x,y=p.y,z=p.z,d1=y-x,d2=z-y;
    if(d==goal||d1==d2)
    {
        dep=d;
        return p;
    }
    if(d1>d2)
    {
        swap(d1,d2);
        int v=min(d2/d1-(d2%d1==0),goal-d);
        return find((node){x,y-v*d1,z-v*d1},d+v,goal);
    }
    int v=min(d2/d1-(d2%d1==0),goal-d);
    return find((node){x+v*d1,y+v*d1,z},d+v,goal);
}
int calc()
{
    int d1,d2,val,l=0,r=inf,dis;
    find(s,0,inf),d1=dep,find(t,0,inf),d2=dep;
    if(d1>d2) s=find(s,0,d1-d2);
    else t=find(t,0,d2-d1);
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(find(s,0,mid)==find(t,0,mid)) dis=mid,r=mid-1;
        else l=mid+1;
    }
    return abs(d1-d2)+dis*2;
}
int main()
{
    s.init(),t.init();
    if(find(s,0,inf)==find(t,0,inf)) printf("YES
%d",calc());
    else puts("NO");
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/14039218.html