首页 > 其他分享 >洛谷 P1081 题解

洛谷 P1081 题解

时间:2023-07-02 19:00:39浏览次数:71  
标签:洛谷 abs int 题解 pos pa P1081 now dp

P1081 [NOIP2012 提高组] 开车旅行 题解

洛谷题目链接

Solution

首先考虑这道题的暴力做法,对于第一问,枚举每个起始点,暴力计算每个点之后最近和第二近的位置,计算答案,最后取最大值。对于第二问,对每个询问独立模拟即可。复杂度较高,无法通过此题。

第一个优化: 考虑到对于固定的当前点和当前驾驶者,接下来的路径是一定的,可以利用倍增来处理。

第二个优化: 可以利用 set 预处理出每个点之后最近和第二近的节点、到达下一个节点的距离。注意需要逆序插入点。如果不希望用如此多的迭代器操作也可以手写平衡树。复杂度为 \(O(n \log n)\)

预处理部分代码如下,\(dp_{i,j,k}\) 表示 \(k\) 从点 \(j\) 继续走 \(2^i\) 次到达的点。
\(disa_{i,j,k}\) 表示 \(k\) 从点 \(j\) 继续走 \(2^i\) 次小 A 行驶的距离。\(disb_{i,j,k}\) 表示 \(k\) 从点 \(j\) 继续走 \(2^i\) 次小 B 行驶的距离。

其中 \(k = 1\) 为小 A, \(k = 2\) 为小 B。

for (int i = n; i; i--)
{
    st.insert({h[i], i});//插入当前点
    auto pos = st.lower_bound({h[i], i});
    --pos;
    // 更小的第一个 位置 / 距离
    int li = pos->second, lh = pos->first;
    ++pos;
    ++pos;
    // 更大的第一个 位置 / 距离
    int nx = pos->second, nh = pos->first;
    --pos;
    int pa, pb;

    // 小的更近
    if (abs(nh - h[i]) >= abs(h[i] - lh))
    {
        pb = li;
        --pos;
        --pos;
        // 第二小的和大的比较
        if (abs(nh - h[i]) >= abs(h[i] - pos->first))
        {
            pa = pos->second;
        }
        else
        {
            pa = nx;
        }
    }
    else //大的更近
    {
        pb = nx;
        ++pos;
        ++pos;
        // 第二大和小的比较
        if (abs(pos->first - h[i]) >= abs(h[i] - lh))
        {
            pa = li;
        }
        else
        {
            pa = pos->second;
        }
    }
    dp[0][i][0] = pa;
    dp[0][i][1] = pb;
    disa[0][i][0] = abs(h[i] - h[pa]);
    disb[0][i][1] = abs(h[i] - h[pb]);
}

接下来处理出所有值,需要注意的是 \(2^0 = 1\) 为奇数,结束时开车的人会改变,需要特判。

for (int i = 1; i <= 18; i++)
{
    for (int j = 1; j <= n; j++)
    {
        for (int k = 0; k <= 1; k++)
        {
            if (i == 1)
            {
                dp[1][j][k] = dp[0][dp[0][j][k]][1 - k];
                disa[1][j][k] = disa[0][j][k] + disa[0][dp[0][j][k]][1 - k];
                disb[1][j][k] = disb[0][j][k] + disb[0][dp[0][j][k]][1 - k];
            }
            else
            {
                dp[i][j][k] = dp[i - 1][dp[i - 1][j][k]][k];
                disa[i][j][k] = disa[i - 1][j][k] + disa[i - 1][dp[i - 1][j][k]][k];
                disb[i][j][k] = disb[i - 1][j][k] + disb[i - 1][dp[i - 1][j][k]][k];
            }
            // cout << "disa[" << i << "][" << j << "][" << k << "]=" << disa[i][j][k] << endl;
            // cout << disa[1][1][0] << endl;
        }
    }
}

求对于给定的 \(x\) 和 \(s\) 两人分别走的距离,很典型的倍增套路。

void solution(int ss, int xx)
{
    int now = ss;
    la = lb = 0;
    for (int i = 18; ~i; i--)
    {
        if (dp[i][now][0] && la + lb + disa[i][now][0] + disb[i][now][0] <= xx)
        {
            la += disa[i][now][0];
            lb += disb[i][now][0];
            now = dp[i][now][0];
        }
    }
}

回答第一问:枚举起点,取最小值。

double ans = 2010000000.000;
int ansid = 0;
for (int i = 1; i <= n; i++)
{
    solution(i, x0);
    double ans__ = (double)la / (double)(lb);
    if (ans__ < ans)
    {
        ans = ans__;
        ansid = i;
    }
    else if (ans__ == ans && h[ansid] < h[i])
    {
        ansid = i;
    }
}
writeln(ansid);

回答第二问:直接调用函数。

for (int i = 1; i <= m; i++)
{
    solution(querys[i].first, querys[i].second);
    writesp(la), writeln(lb);
}

Codes

#include <bits/stdc++.h>
using namespace std;
#define max_n 110101
void read(int &p)
{
    p = 0;
    int k = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
        {
            k = -1;
        }
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        p = p * 10 + c - '0';
        c = getchar();
    }
    p *= k;
    return;
}
void write_(int x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x > 9)
    {
        write_(x / 10);
    }
    putchar(x % 10 + '0');
}
void writesp(int x)
{
    write_(x);
    putchar(' ');
}
void writeln(int x)
{
    write_(x);
    putchar('\n');
}
int n, h[max_n], x0, m;
set<pair<int, int>> st;
pair<int, int> querys[max_n];
int dp[19][max_n][3];
int disa[19][max_n][3];
int disb[19][max_n][3];
int la, lb;
void solution(int ss, int xx)
{
    int now = ss;
    la = lb = 0;
    for (int i = 18; ~i; i--)
    {
        if (dp[i][now][0] && la + lb + disa[i][now][0] + disb[i][now][0] <= xx)
        {
            // cout << i << " " << now << " " << disa[1][1][0] << endl;
            la += disa[i][now][0];
            lb += disb[i][now][0];
            now = dp[i][now][0];
        }
    }
    // cout << "#" << la << " " << lb << endl;
}
signed main()
{
#if _clang_
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
#endif
    read(n);
    for (int i = 1; i <= n; i++)
    {
        read(h[i]);
    }
    read(x0), read(m);
    for (int i = 1; i <= m; i++)
    {
        read(querys[i].first);
        read(querys[i].second);
    }
    h[0] = 2000000000;
    h[n + 1] = -2000000000;
    st.insert({h[0], 0});
    st.insert({h[n + 1], n + 1});
    for (int i = n; i; i--)
    {
        st.insert({h[i], i});
        auto pos = st.lower_bound({h[i], i});
        --pos;
        int li = pos->second, lh = pos->first;
        ++pos;
        ++pos;
        int nx = pos->second, nh = pos->first;
        --pos;
        int pa, pb;
        //   cout << "@" << li << " " << lh << " " << nx << " " << nh << endl;
        if (abs(nh - h[i]) >= abs(h[i] - lh))
        {
            //   cout << "A" << endl;
            pb = li;
            --pos;
            --pos;
            if (abs(nh - h[i]) >= abs(h[i] - pos->first))
            {
                pa = pos->second;
            }
            else
            {
                pa = nx;
            }
        }
        else
        {
            // cout << "B" << endl;
            pb = nx;
            ++pos;
            ++pos;
            if (abs(pos->first - h[i]) >= abs(h[i] - lh))
            {
                pa = li;
            }
            else
            {
                pa = pos->second;
            }
        }
        dp[0][i][0] = pa;
        dp[0][i][1] = pb;
        disa[0][i][0] = abs(h[i] - h[pa]);
        disb[0][i][1] = abs(h[i] - h[pb]);
        //  cout << dp[0][i][0] << " " << dp[0][i][1] << " " << disa[0][i][0] << " " << disb[0][i][1] << endl;
    }
    for (int i = 1; i <= 18; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            for (int k = 0; k <= 1; k++)
            {
                if (i == 1)
                {
                    dp[1][j][k] = dp[0][dp[0][j][k]][1 - k];
                    disa[1][j][k] = disa[0][j][k] + disa[0][dp[0][j][k]][1 - k];
                    disb[1][j][k] = disb[0][j][k] + disb[0][dp[0][j][k]][1 - k];
                }
                else
                {
                    dp[i][j][k] = dp[i - 1][dp[i - 1][j][k]][k];
                    disa[i][j][k] = disa[i - 1][j][k] + disa[i - 1][dp[i - 1][j][k]][k];
                    disb[i][j][k] = disb[i - 1][j][k] + disb[i - 1][dp[i - 1][j][k]][k];
                }
                // cout << "disa[" << i << "][" << j << "][" << k << "]=" << disa[i][j][k] << endl;
                // cout << disa[1][1][0] << endl;
            }
        }
    }
    double ans = 2010000000.000;
    int ansid = 0;
    for (int i = 1; i <= n; i++)
    {
        solution(i, x0);
        // cout << la << " " << lb << endl;
        double ans__ = (double)la / (double)(lb);
        if (ans__ < ans)
        {
            ans = ans__;
            ansid = i;
        }
        else if (ans__ == ans && h[ansid] < h[i])
        {
            ansid = i;
        }
    }
    writeln(ansid);
    for (int i = 1; i <= m; i++)
    {
        solution(querys[i].first, querys[i].second);
        writesp(la), writeln(lb);
    }
    return 0;
}

标签:洛谷,abs,int,题解,pos,pa,P1081,now,dp
From: https://www.cnblogs.com/yuhang-ren/p/17521199.html

相关文章

  • docker启动RabbitMQ以及常见问题解决
    docker启动MQ容器下载docker镜像dockersearchrabbitmqdockerpullrabbitmqdockerrun-d--hostnamemy-rabbit--namerabbit-p15672:15672-p5672:5672rabbitmq:latest启动容器后浏览器无法访问dockerexec-it3b124f0c9712/bin/bashrabbitmq-pluginsenab......
  • CF842E Nikita and game 题解
    题意一棵树初始只有一个编号为1的根结点。\(n\)次操作,每次新增一个点作为\(p_i\)的子结点,询问更新后有多少点可以作为树直径的端点。\(n\le3\times10^5\)。题解以下\(dist(x,y)\)表示点\(x\)与点\(y\)在树上的距离。不难发现若干条直径必然叠合于至少一点,任选这......
  • Codeforces Round 881 Div2 A-F1题解
    codeforcesround881div2题解马上要秋招了,自己本事全丢了,感觉如果这样的话今年就估计要饿死了。先打div3,7月份得开始收心了A.SashaandArrayColoring题意,可以分任意组,每组的贡献是max-min,问最大贡献显然是贪心,从大到小配对一下就行,不想放代码了’B.LongLong给出一......
  • JSON中,java.lang.NoClassDefFoundError: net/sf/ezmorph/Morpher问题解决
    使用JSON,在SERVLET或者STRUTS的ACTION中取得数据时,如果会出现异常:java.lang.NoClassDefFoundError:net/sf/ezmorph/Morpher是因为需要的类没有找到,一般,是因为少导入了JAR包,使用JSON时,除了要导入JSON网站上面下载的json-lib-2.2-jdk15.jar包之外,还必须有其它几个依赖包:commons-bean......
  • 「解题报告」2023.7.1 洛谷7月月赛
    『XYGOIround1』三个数题目描述MX有一个有\((w-2)\)个数的集合\(S=\{3,4,5,\cdots,w\}\)。要求构造一个只包含非负整数的集合(无重复元素),使得\(S\)里面的任何一个数都能被这个集合里面大于等于\(3\)个不同的数相加得到,求这个集合中至少包含多少个元素。输入格式本题......
  • dpkg-reconfigure命令找不到问题解决
    作者: adminhttps://cloudbool.com/archive/dpkg-reconfigure-command-not-found.html今天在SSH远程连接到服务器时,遇到了dpkg-reconfigure命令找不到的问题,觉得很是奇怪,花了点时间研究下,这里做个记录,以备后用。前情提要我远程的服务器系统是自行使用DebiannetinstISO镜......
  • [ABC306] C/D 题解
    C.Centers 参考算法统计,排序\(\rmSyx\)给你\(3\timesN\)个数,其中\(\rmSyx\)可以保证:\(1\simN\)之间的所有数都出现了\(3\)次,请你将\(1\simN\)之间的数每个出现在第\(2\)个位置的下标进行排序,并从小到大输出原数。用\(\rmmap\)记录数出现的次数,......
  • 【题解】#119. 最大整数 题解(2023-07-01更新)
    #119.最大整数题解题目传送门更新日志2023-05-2617:20文章完成2023-05-3015:22文章审核通过2023-07-0116:04修改了代码题目知识点字符串+贪心题意说明设有n个正整数($n<20$),将它们连接成一排,组成一个最大的多位整数。(题目简介明了,一看就是出题人懒得写题目背景)......
  • 【题解】P8679 [蓝桥杯 2019 省 B] 填空问题 题解
    P8679[蓝桥杯2019省B]填空问题题解题目传送门欢迎大家指出错误并联系这个蒟蒻更新日志2023-05-2521:02文章完成2023-05-2711:34文章通过审核2023-06-2021:03优化了文章代码格式试题A:组队【解析】本题是一道经典的DFS搜索题,每次对各号位的选手进行DFS,......
  • 【题解】P8741 [蓝桥杯 2021 省 B] 填空问题 题解
    P8741[蓝桥杯2021省B]填空问题题解题目传送门欢迎大家指出错误并联系这个蒟蒻更新日志2023-05-0923:19文章完成2023-05-0923:20通过审核2023-06-2021:03优化了文章代码格式试题A:空间【解析】本题考察计算机存储的基础知识,只要掌握空间存储的换算方法,就能......