1. 程式人生 > >luogu1081 開車旅行 樹上倍增

luogu1081 開車旅行 樹上倍增

turn 後繼 des 樹上倍增 mes 旅行 倍增 輸出 r+

題目大意

  小A和小B決定利用假期外出旅行,他們將想去的城市從1到N編號,且編號較小的城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市i 的海拔高度為Hi,城市i 和城市j 之間的距離d[i,j]恰好是這兩個城市海拔高度之差的絕對值,即d[i,j] = |Hi – Hj|。
  旅行過程中,小A和小B輪流開車,第一天小A開車,之後每天輪換一次。他們計劃選擇一個城市S作為起點,一直向東行駛,並且最多行駛X公裏就結束旅行。小A和小B的駕駛風格不同,小B總是沿著前進方向選擇一個最近的城市作為目的地,而小A總是沿著前進方向選擇第二近的城市作為目的地(註意:本題中如果當前城市到兩個城市的距離相同,則認為離海拔低的那個城市更近)。如果其中任何一人無法按照自己的原則選擇目的城市,或者到達目的地會使行駛的總距離超出X公裏,他們就會結束旅行。
  在啟程之前,小A想知道兩個問題:
  1.對於一個給定的X=X0,從哪一個城市出發,小A開車行駛的路程總數與小B行駛的路程總數的比值最小(如果小B的行駛路程為0,此時的比值可視為無窮大,且兩個無窮大視為相等)。如果從多個城市出發,小A開車行駛的路程總數與小B行駛的路程總數的比值都最小,則輸出海拔最高的那個城市。
 

 2. 對任意給定的X=Xi 和出發城市Si,小A開車行駛的路程總數以及小B行駛的路程總數。

題解

如何處理對小A小B一個城市的下一個城市

  法一:二叉平衡樹,key值城市高度。從右往左掃描,見一個城市就往裏塞,然後從節點的前驅、後繼以及前驅的前驅、後繼的後繼中選取最小值和次小值。這個可以用set的iterator來實現。

  法二:雙向鏈表。將所有城市排序接成鏈表,從左往右掃描,在cur->Prev->Prev, cur->Prev, cur->Next, cur->Next->Next中選取最小值和次小值,然後將其刪除。

如何求解

  樹上倍增。將所有城市復制一份,一份表示A開車,一份表示B開車,根據A,B城市之間的轉移在兩個集合之間連邊,出度為0的節點向根節點連邊。邊權有3個:A行駛距離,B行駛距離(在連接A, B集合的邊中,此兩個量必然有一個為0),總行駛距離。對這三個權值進行倍增統計,隨後各個事情就簡單了。

註意事項

  對於這種題意復雜的題,最好把限制條件寫在紙上,不然記著記著就記錯了,浪費大把時間。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <cmath>
#include <vector>
using namespace std;

const int MAX_CITY = 100010, SuperINF = 2147483647;

struct City
{
    int Height, Id;

    bool operator < (const City& a) const
    {
        return Height < a.Height;
    }
}_cities[MAX_CITY];
int TotCity;

struct Graph
{
private:
    static const int MAX_NODE = MAX_CITY * 2 + 50;

    struct Node
    {
        unsigned int Sum[3][20];
        Node *Elder[20];
        vector<Node*> Next;
        int Depth;
    }_nodes[MAX_NODE], *Root;
    int TotNode;

    void Dfs(Node *cur, Node *fa, int depth)
    {
        cur->Depth = depth;
        if (cur != Root)
        {
            for (int i = 1; cur->Elder[i - 1]->Elder[i - 1]; i++)
            {
                cur->Elder[i] = cur->Elder[i - 1]->Elder[i - 1];
                for (int j = 0; j < 3; j++)
                    cur->Sum[j][i] = cur->Sum[j][i - 1] + cur->Elder[i - 1]->Sum[j][i - 1];
            }
        }
        for (int i = 0; i < cur->Next.size(); i++)
            if (cur->Next[i] != fa)
                Dfs(cur->Next[i], cur, depth + 1);
    }

    int Log2(int x)
    {
        int ans = 0;
        while (x >>= 1)
            ans++;
        return ans;
    }

    void Query(Node *cur, int dist, int *ans)
    {
        for (int i = 0; i < 3; i++)
            ans[i] = 0;
        int topFa = Log2(cur->Depth);
        for (int i = topFa; i >= 0; i--)
        {
            if (cur->Elder[i] && cur->Sum[2][i] <= dist)
            {
                for (int j = 0; j < 3; j++)
                    ans[j] += cur->Sum[j][i];
                dist -= cur->Sum[2][i];
                cur = cur->Elder[i];
            }
        }
    }

public:
    void Init(int totNode, int root)
    {
        TotNode = totNode;
        Root = _nodes + root;
    }

    void Build(int u, int v, int w, bool isA)
    {
        Node *cur = _nodes + u, *fa = _nodes + v;
        cur->Elder[0] = fa;
        fa->Next.push_back(cur);
        if (isA)
            cur->Sum[0][0] = cur->Sum[2][0] = w;
        else
            cur->Sum[1][0] = cur->Sum[2][0] = w;
    }

    void GetSum()
    {
        Dfs(Root, NULL, 0);
    }

    void Query(int start, int dist, int *ans)
    {
        return Query(_nodes + start, dist, ans);
    }
}g;

int GetLDelta(set<City>& tree, set<City>::iterator cur)
{
    if (cur == tree.begin())
        return SuperINF;
    set<City>::iterator l = cur;
    l--;
    return abs(cur->Height - l->Height);
}

int GetRDelta(set<City>& tree, set<City>::iterator cur)
{
    set<City>::iterator r = cur;
    r++;
    if (r == tree.end())
        return SuperINF;
    return abs(cur->Height - r->Height);
}

void BuildGraph()
{
    g.Init(TotCity * 2 + 1, TotCity * 2 + 1);
    static set<City> tree;
    for (int i = TotCity; i >= 1; i--)
    {
        tree.insert(_cities[i]);
        set<City>::iterator cur = tree.find(_cities[i]);
        unsigned int lDelta = GetLDelta(tree, cur);
        unsigned int rDelta = GetRDelta(tree, cur);
        if (lDelta < SuperINF || rDelta < SuperINF)
        {
            if (lDelta <= rDelta)
            {
                set<City>::iterator l = cur;
                l--;
                g.Build(cur->Id + TotCity, l->Id, lDelta, false);
                unsigned int llDelta = GetLDelta(tree, l) + lDelta;
                if (llDelta < SuperINF || rDelta < SuperINF)
                {
                    if (llDelta <= rDelta)
                    {
                        l--;
                        g.Build(cur->Id, l->Id + TotCity, llDelta, true);
                    }
                    else
                    {
                        set<City>::iterator r = cur;
                        r++;
                        g.Build(cur->Id, r->Id + TotCity, rDelta, true);
                    }
                }
                else
                    g.Build(cur->Id, TotCity * 2 + 1, SuperINF, true);
            }
            else
            {
                set<City>::iterator r = cur;
                r++;
                g.Build(cur->Id + TotCity, r->Id, rDelta, false);
                unsigned int rrDelta = GetRDelta(tree, r) + rDelta;
                if (lDelta < SuperINF || rrDelta < SuperINF)
                {
                    if (lDelta <= rrDelta)
                    {
                        set<City>::iterator l = cur;
                        l--;
                        g.Build(cur->Id, l->Id + TotCity, lDelta, true);
                    }
                    else
                    {
                        r++;
                        g.Build(cur->Id, r->Id + TotCity, rrDelta, true);
                    }
                }
                else
                    g.Build(cur->Id, TotCity * 2 + 1, SuperINF, true);
            }
        }
        else
        {
            g.Build(cur->Id + TotCity, TotCity * 2 + 1, SuperINF, true);
            g.Build(cur->Id, TotCity * 2 + 1, SuperINF, false);
        }
    }
    g.GetSum();
}

void Read()
{
    scanf("%d", &TotCity);
    for (int i = 1; i <= TotCity; i++)
    {
        scanf("%d", &_cities[i].Height);
        _cities[i].Id = i;
    }
}

void Sol1()
{
    int dist0, ansStart = 0;
    double ansRatio = SuperINF;
    scanf("%d", &dist0);
    int ans[3];
    for (int i = 1; i <= TotCity; i++)
    {
        g.Query(i, dist0, ans);
        double curRatio = (ans[1] == 0 ? SuperINF : 1.0 * ans[0] / ans[1]);
        if (curRatio < ansRatio || (abs(curRatio - ansRatio) < 0.0000001 && _cities[i].Height > _cities[ansStart].Height))
        {
            ansRatio = curRatio;
            ansStart = i;
        }
    }
    printf("%d\n", ansStart);
}

void Sol2()
{
    int qCnt;
    int ans[3];
    scanf("%d", &qCnt);
    while (qCnt--)
    {
        int start, dist;
        scanf("%d%d", &start, &dist);
        g.Query(start, dist, ans);
        printf("%d %d\n", ans[0], ans[1]);
    }
}

int main()
{
    Read();
    BuildGraph();
    Sol1();
    Sol2();
    return 0;
}

  

luogu1081 開車旅行 樹上倍增