【hdu3830】Checkers

本文详细解析了HDU 3830数轴移动问题,介绍了一种利用二叉树模型解决问题的方法,并给出了具体的实现思路及代码。文章还讨论了如何高效地找到树根以及求解两点间的距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=3830
题目大意:在数轴上有三个互不相等的数,对于两个数x和y,x可以移动到它关于y对称的位置x’,当且仅当从x到x’这段区间内没有第三个数。问状态a,b,c能否经过若干次移动变成状态x,y,z。如果能,输出一行YES,第二行输出最小移动次数,否则输出NO。输入有多组数据。
数据范围:a,b,c,x,y,z∈(-10^9,10^9)

题解:首先我们先将a,b,c三个数排序。可以知道,b向两边移动一定是合法的,而如果两边的数向中间移动,只有与b较近的数移动合法,当|a-b|=|b-c|时,则a和c均不能向中间移动。我们发现,这就是一个二叉树的模型,而且是一棵无穷大的二叉树,每个节点有三个参数(a,b,c),当|a-b|=|b-c|时即为树根。题目所求的就是(a,b,c)与(x,y,z)在树上的距离。注意两个点可能在不同的树上,所以要先找到它们的树根,若不同则无解,否则找两个点的公共祖先计算距离。
那么如何找树根呢?暴力肯定是不行的,比如(1,2,999999999),暴力移动时间复杂度将达到10^9,所以我们需要找到更快的方法。
举个例子,(a,b,c)=(1,4,15)。
第一次移动,1→7,(4,7,15);
第二次移动,4→10,(7,10,15);
第三次移动,7→13,(10,13,15);
第四次移动,15→11,(10,11,13);
第五次移动,10→12,(11,12,13)。
观察上面的过程可以发现,一开始|a-b|<|b-c|,所以a和b同时向c的方向移动,直到|a-b|>=|b-c|,然后是b和c同时向a移动,这样交替直到|a-b|=|b-c|。于是我们发现这个过程与辗转相除法相似:当|a-b|<|b-c|时,将a和b同时向右移|b-c|/|a-b|步,每步长|a-b|,反之同理。当|b-c|%|a-b|=0时应少移一步。
这样我们可以在log次以内求出某个点到根的距离。求两个点的最近公共祖先时,先把距离根较远的点上移到与另一点同等的位置,然后二分判断他们到最近公共祖先的距离。至此,题目解决。
时间复杂度O(log^2)

代码如下:

#include <algorithm>
#include <cstdio>
struct P{
    int a,b,c;
    void Sort()
    {
        if (a>b) std::swap(a,b);
        if (a>c) std::swap(a,c);
        if (b>c) std::swap(b,c);
    }
    int findrt()
    {
        int d=0;
        for (int x,y;b-a!=c-b;)
        if (b-a<c-b)
        {
            x=b-a;
            y=(c-b-1)/x;
            d+=y;
            a+=x*y;
            b+=x*y;
        }
        else
        {
            x=c-b;
            y=(b-a-1)/x;
            d+=y;
            c-=x*y;
            b-=x*y;
        }
        return d;
    }
    void up(int d)
    {
        for (int y,z;d;d-=y)
        if (b-a<c-b)
        {
            z=b-a;
            y=(c-b-1)/z;
            if (y>d) y=d;
            a+=z*y;
            b+=z*y;
        }
        else
        {
            z=c-b;
            y=(b-a-1)/z;
            if (y>d) y=d;
            c-=z*y;
            b-=z*y;
        }
    }
}A,B,C,D;
bool operator ==(P A,P B){
    return A.a==B.a && A.b==B.b && A.c==B.c;
}
int main(){
    for (;~scanf("%d%d%d%d%d%d\n",&A.a,&A.b,&A.c,&B.a,&B.b,&B.c);)
    {
        A.Sort();B.Sort();
        C=A;D=B;
        int da=A.findrt(),db=B.findrt();
        if (!(A==B)) printf("NO\n");
        else
        {
            if (da>db) C.up(da-db);
                else D.up(db-da);
            int l=0,r=std::min(da,db),mid,ans;
            for (;l<=r;)
            {
                mid=(l+r)>>1;
                A=C;B=D;
                A.up(mid);B.up(mid);
                if (A==B) ans=mid,r=mid-1;
                    else l=mid+1;
            }
            printf("YES\n%d\n",2*ans+std::max(da,db)-std::min(da,db));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值