luogu1081 开车旅行 树上倍增

题目大意

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

  

原文地址:https://www.cnblogs.com/headboy2002/p/9550694.html

时间: 2024-10-10 22:11:00

luogu1081 开车旅行 树上倍增的相关文章

习题:开车旅行(倍增+预处理)

开车旅行(NOIP提高组2012) [描述]小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市i 的海拔高度为Hi,城市i 和城市j 之间的距离d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i,j] = |Hi - Hj|.旅行过程中,小A和小B轮流开车,第一天小A开车,之后每天轮换一次.他们计划选择一个城市S作为起点,一直向东行驶,并且最多行驶X公里就结束旅行.小A和小B的驾驶风格不同,小B总是

luogu1081 开车旅行2012 D1T3 (倍增,set,O2)

题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i,j] = |Hi? Hj|. 旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次.他们计划选择一个城市 S 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行.小 A 和小 B的驾驶风

$Noip2012\ Luogu1081$ 开车旅行(倍增优化$ DP$)

Luogu Description Sol 1.发现对于每个城市,小A和小B的选择是固定的,可以预处理出来,分别记为ga[],gb[] 2.并且,只要知道了出发城市和出发天数,那么当前城市和小A,小B各行驶的路程也是一定的,同样可以分别预处理出来 具体怎么预处理: 1.其实就是"邻值查找"    简单讲一下,就是把所有城市的高度都存进set排好序,然后ga[i]一定是在set里与h[i]相邻的中最近的的,gb[i]是与h[i]相邻的中次近的 2.倍增优化: 1) 设$p[i][j][k

NOIP2012开车旅行 【倍增】

题目 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i,j] = |Hi? Hj|. 旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次.他们计划选择一个城市 S 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行.小 A 和小 B的驾驶风格不

洛谷 P1086 开车旅行 【倍增+STL】

题目: https://www.luogu.org/problem/show?pid=1081 分析: 这题第一眼给人的感觉就是要模拟,模拟两人交替开车,分别预处理出离特定城市第一近和第二近的(用set).实际上就是这样,只不过用set和倍增优化了一下,用: g[i][k]表示从位置i开始,两人轮流开2^k轮车最后到达的位置: f[i][j][0] 表示表示从位置i开始,两人轮流开2^k轮车最后小A走过的距离: f[i][j][1] 表示表示从位置i开始,两人轮流开2^k轮车最后小B走过的距离:

Luogu1081 开车旅行

代码巨长的倍增题... 显然这是没有什么决策的,选择方案都是固定的 这可以考虑倍增跳,每个位置上跳到的位置是可以通过查前驱后继解决的 有两种方式: 一种是平衡树搞法,倒着做查完了插入 另一种是先排序建一个链表,查完了删除 都是可以通过加哨兵节点来搞的,结果我只想到了 set 乱搞,就写了很长 预处理完就可做了 第一问对于每个点倍增一遍,其实就是照题意模拟,倍增优化一下 第二问还是照题意模拟,倍增优化一下 暴力有 70pts ? 代码: #include <algorithm> #include

【noip2012】开车旅行 [倍增]

开车旅行 倍增 详细版 可以发现每个起点出发后面选择的城市都是一定的 所以预处理出\(to[i][j],da[i][j],db[i][j]\)表示从\(i\)出发小\(A\)和小\(B\)经过\(2^j\)轮后到达的地点.小\(A\)走的路程.小\(B\)走的路程 预处理时询问第一近和第二近的地点用双向链表 双向链表 不仅记录当前元素的下一个元素\((next)\)还记录当前元素的上一个元素\((pre)\) 数组版: int head,tail,tot; struct Node{ int va

开车旅行(ST表)

题意 有两个人轮流开车,从起点S出发向东走,距离就是两个位置的高度差的绝对值,A喜欢第二近的地方,B喜欢最近的地方.他们会停止旅行当且仅当找不到下个满足条件的位置或者再走的话总路程会超过x.A先开车. 有两个问题: 1.对于给定的x,求在哪个起点出发,A与B路程比最小,disB=0的话视为无穷大,(oo=oo),相同的话取高度最高的位置. 2.给出m个询问,给出S,x,求A,B的行驶路程. 题解 首先考虑先求出A,B在每个位置出发的下个位置,给出一种做法(其实是抄的题解):先按高度排序,对于原序

noip2012 开车旅行

P1081 开车旅行 139通过 484提交 题目提供者洛谷OnlineJudge 标签倍增2012NOIp提高组 难度提高+/省选- 提交该题 讨论 题解 记录 题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的 城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即 d[i,j] = |Hi− Hj|. 旅行过程中,小