1. 程式人生 > >Checkers 【HDU - 3830】【LCA+二分答案+輾轉相除】

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