Checkers 【HDU - 3830】【LCA+二分答案+辗转相除】

题目链接


  题意:我们有(x, y, z)与(a, b, c)这三个点,问,每次可以条一颗棋子,使得新跳的棋子到的位置与原先位置的平均数是中间棋子的位置数。例如:(1, 3, 7)可以跳成(3, 5, 7),就是把“1”以“3”为中间的跳板,跳到“5”这个位置上去的。

  很好的一道思维LCA的题,这道题,我们可以化成一棵树来想,关键是在于怎么建立这棵树,我们可以举个样例来看,我们可以发现(3,5,7)的点中“3”、“7”是没法跳的,我们只能挪动“5”这个节点,只能把“5”向左或者向右跳。那么我们就可以看成这样:把(x, y, z)中(y-x)==(z-y)的点看作稳态点,也可以叫作是根节点,然后它旗下的点都是以它为基础,向左或者向右得到的,并且,旗下的点回归根节点的方式,就是把外面的点往中间放来得到的。

  那么,假如我们去跑O(N)的复杂度显然是不行的,我们得做出优化,那么优化的过程得在图上实现,假如遇到(0, 100 50000)这样的点,我们应该怎样去优化?看到(100-0=100)与(50000-100=49900),不难发现,原点可以这样子移动:(100, 200, 50000)->(200, 300, 50000)->……->(49800, 49900, 50000),那么,这是怎样推出来的呢?用到了辗转相除。为了逼近(y-x)==(z-y)的状态,我们得去找路去靠近它,那么对于(0,100,50000),可以看作(y-x==100)与(z-y==49900),为了靠近,就需要x、y都移动到50000的前一个状态,不就是可以看成是49900/100,但是因为是整除,会发现最后就有y完完全全的靠在了z上面,所以,我们得处理“-1”,与其最后去减,我们不如做(len_max - 1)/len_min的优化,仔细想想,不就是把整除的向前移了一位吗?然后,不断做这样的辗转相除,知道两边的长度相等,达到根节点时,记录下路径,就是每次处理(len_max - 1)/len_min的步数。

  剩下的,就是怎么求最近的祖先了,为什么这么说?我们将他们放到了同一棵树上去,如果不等,就是不在同一棵树上,那么就是“NO”,否则,就是向上找最近公共祖先的处理方式,我们利用之前所求出来的深度(即到根节点的路径),我们先放至等深度的高度上,然后二分答案从根节点下来的路径,去查找这样的根节点是否满足,即可。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
struct node
{
    int x, y, z, deep;
    node(int a=0, int b=0, int c=0, int d=0):x(a), y(b), z(c), deep(d) {}
}S, T;
bool cmp_same(node e1, node e2) { return e1.x == e2.x && e1.y == e2.y && e1.z == e2.z; }
void SORT(node &X)
{
    if(X.x > X.y) swap(X.x, X.y);
    if(X.x > X.z) swap(X.x, X.z);
    if(X.y > X.z) swap(X.y, X.z);
}
node Root_node(node &X)
{
    node ans = X;
    int leny_x = 0, lenz_y = 0;
    while((leny_x = ans.y - ans.x) != (lenz_y = ans.z - ans.y))
    {
        int tmp = 0;
        if(leny_x > lenz_y)
        {
            tmp = (leny_x - 1)/lenz_y;
            ans.y -= tmp * lenz_y;
            ans.z -= tmp * lenz_y;
        }
        else
        {
            tmp = (lenz_y - 1)/leny_x;
            ans.x += tmp * leny_x;
            ans.y += tmp * leny_x;
        }
        ans.deep += tmp;
    }
    X.deep = ans.deep;
    return ans;
}
void update(node &X, int deth)
{
    int moved = 0;
    while(moved < deth)
    {
        int at_most = deth - moved;
        int leny_x = X.y - X.x, lenz_y = X.z - X.y;
        if(leny_x > lenz_y)
        {
            int tmp = (leny_x - 1)/lenz_y;
            tmp = min(tmp, at_most);
            moved += tmp;
            X.y -= tmp * lenz_y;
            X.z -= tmp * lenz_y;
        }
        else
        {
            int tmp = (lenz_y - 1)/leny_x;
            tmp = min(tmp, at_most);
            moved += tmp;
            X.x += tmp * leny_x;
            X.y += tmp * leny_x;
        }
    }
    X.deep -= deth;
}
int solve(int L, int R)
{
    int ans = S.deep, mid = (L + R)>>1, high = 0;
    node tmp1, tmp2;
    while(L <= R)
    {
        mid = (L + R)>>1;
        tmp1 = S;   tmp2 = T;
        high = S.deep - mid;
        update(tmp1, high);
        update(tmp2, high);
        if(!cmp_same(tmp1, tmp2)) R = mid - 1;
        else { L = mid + 1; ans = mid; }
    }
    return 2*(S.deep - ans);
}
int main()
{
    while(scanf("%d%d%d", &S.x, &S.y, &S.z)!=EOF)
    {
        scanf("%d%d%d", &T.x, &T.y, &T.z);
        SORT(S);    SORT(T);
        S.deep = T.deep = 0;
        node RS = Root_node(S), RT = Root_node(T);
        if(!cmp_same(RS, RT)) { printf("NO\n"); continue; }
        printf("YES\n");
        int det_h = abs(S.deep - T.deep);   //处理到等深度,所需要的花费
        if(S.deep > T.deep) update(S, det_h);
        else if(S.deep < T.deep) update(T, det_h);
        printf("%d\n", det_h + solve(0, S.deep));
    }
    return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值