洛谷 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走过的距离;

容易得到:

nxt[i][1]表示离i第二近的城市编号

nxt[i][0]表示离i第一近的城市编号

dis[i][1]表示离i第二近的城市编号

dis[i][0]离i第一近的城市编号

初始化:

  g[i][0]=nxt[nxt[i][1]][0];  //从i开始两人轮流开一轮到的地方
        f[i][0][0]=dis[i][1];  //开一次b走的路径
        f[i][0][1]=dis[nxt[i][1]][0];  //开一次a走的路径

递推:

g[i][j]=g[g[i][j-1]][j-1];     //从第一近到第二近
            f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];    //A走一轮,再走一轮
            f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];   //B走一轮,再走一轮

下面是参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
#define maxn 100000+10
using namespace std;
struct point
{
    int num,h;
    bool operator < (const point ano) const
    {
        return h<ano.h;
    }
}city[maxn];
set<point> S;
set<point> :: iterator it;
long long f[maxn][21][2];
int nxt[maxn][2],dis[maxn][2],g[maxn][21];
int n,x0,m;

void update(point x,point y)
{
    if(!nxt[x.num][0])
    {
        nxt[x.num][0]=y.num;
        dis[x.num][0]=abs(x.h-y.h);
    }
    else if(dis[x.num][0]>abs(x.h-y.h)||(dis[x.num][0]==abs(x.h-y.h)&&y.h<city[nxt[x.num][0]].h))
    {
        nxt[x.num][1]=nxt[x.num][0];
        dis[x.num][1]=dis[x.num][0];
        nxt[x.num][0]=y.num;
        dis[x.num][0]=abs(x.h-y.h);
    }
    else if(dis[x.num][1]>abs(x.h-y.h)||(dis[x.num][1]==abs(x.h-y.h)&&y.h<city[nxt[x.num][1]].h))
    {
        nxt[x.num][1]=y.num;
        dis[x.num][1]=abs(x.h-y.h);
    }
    else if(!nxt[x.num][1])
    {
        nxt[x.num][1]=y.num;
        dis[x.num][1]=abs(x.h-y.h);
    }
    return;
}

void query(int s,int x,long long &dista,long long &distb)
{
    for(int i=20;i>=0;i--)
        if(f[s][i][0]+f[s][i][1]<=x&&g[s][i])//从大到小,能塞就塞
        {
            dista+=f[s][i][0];
            distb+=f[s][i][1];
            x-=f[s][i][1]+f[s][i][0];
            s=g[s][i];
        }
    if(nxt[s][1]&&dis[s][1]<=x)
        dista+=dis[s][1];
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&city[i].h);
        city[i].num=i;
    }
    for(int i=n;i>=1;i--)
    {
        S.insert(city[i]);
        it=S.find(city[i]);
        if(it!=S.begin())
        {
            it--;
            update(city[i],*it);
            if(it!=S.begin())
            {
                it--;
                update(city[i],*it);
                it++;
            }
            it++;
        }
        if((++it)!=S.end())
        {
            update(city[i],*it);
            if((++it)!=S.end())
            {
                update(city[i],*it);
                it--;
            }
            it--;
        }
    }
    for(int i=1;i<=n;i++)//初始化
    {
        g[i][0]=nxt[nxt[i][1]][0];//从i开始两人轮流开一轮到的地方
        f[i][0][0]=dis[i][1];//开一次b走的路径
        f[i][0][1]=dis[nxt[i][1]][0];//开一次a走的路径
    }
    for(int j=1;j<=20;j++)
        for(int i=1;i<=n;i++)
        {
            g[i][j]=g[g[i][j-1]][j-1];//从第一近到第二近
            f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];//A走一轮,再走一轮
            f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];//B走一轮,再走一轮
        }
    scanf("%d",&x0);
    int s0=0;
    long long a=1e15,b=0;
    for(int i=1;i<=n;i++)
    {
        long long dista=0,distb=0;
        query(i,x0,dista,distb);
        if(distb&&(!s0||(dista*b<distb*a)))
        {
            s0=i;
            a=dista;
            b=distb;
        }
    }
    printf("%d\n",s0);
    scanf("%d",&m);
    while(m--)
    {
        long long dista=0,distb=0;
        int s,x;
        scanf("%d%d",&s,&x);
        query(s,x,dista,distb);
        printf("%d %d\n",dista,distb);
    }
    return 0;
}

 

时间: 2024-08-25 23:16:20

洛谷 P1086 开车旅行 【倍增+STL】的相关文章

洛谷P1081 开车旅行

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

[NOIP2012] 提高组 洛谷P1081 开车旅行

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

【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

$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

P1081 开车旅行[倍增](毒瘤题)

其实就是个大模拟. 首先,根据题意,小A和小B从任意一个城市开始走,无论\(X\)如何,其路径是一定唯一的. 显然对于两问都可以想出一个\(O(n^2)\)的暴力,即直接一步一步地向右走. 首先,我们当然需要知道A,B在每个城市的下一步如何走,记\(nexta(i),nextb(i)\)为A,B在\(i\)处时,下一步走到的城市编号. 考虑如何高效(复杂度小于等于\(O(nlogn)\))维护两个\(next\). 显然不能直接维护每个城市与其后面的城市的差值,再好的数据结构也会到\(O(n^2

【NOIP2012提高组】开车旅行 倍增

题目分析 朴素的做法就是预处理下一个目的地,然后跑模拟,超时. 本题最重要的考点是倍增优化.设$fa[i][j]$表示a从i出发行驶$2^j$“次”后行驶的路程,$fb[i][j]$表示从i出发行驶$2^j$“次”后行驶的路程,注意这里的"次",a.b交替行驶.$f[i][j]$表示从i出发a.b交替$2^j$“次”后行驶到的城市编号. 显然有$fa[i][j] = fa[i][j - 1] + fa[f[i][j - 1]][j - 1], fb = fb[i][j - 1] + f

洛谷——P1086 花生采摘

P1086 花生采摘 题目描述 鲁宾逊先生有一只宠物猴,名叫多多.这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生!――熊字”. 鲁宾逊先生和多多都很开心,因为花生正是他们的最爱.在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成矩形网格(如图1).有经验的多多一眼就能看出,每棵花生植株下的花生有多少.为了训练多多的算术,鲁宾逊先生说:“你先找出花生最多的植株,去采摘它的花生:然后再找出剩下的植株里花生最多的,去采摘它的花生:依此类推,不

洛谷 P1086 花生采摘

P1086 花生采摘 题目描述 鲁宾逊先生有一只宠物猴,名叫多多.这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生!――熊字”. 鲁宾逊先生和多多都很开心,因为花生正是他们的最爱.在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成矩形网格(如图1).有经验的多多一眼就能看出,每棵花生植株下的花生有多少.为了训练多多的算术,鲁宾逊先生说:“你先找出花生最多的植株,去采摘它的花生:然后再找出剩下的植株里花生最多的,去采摘它的花生:依此类推,不

CH5701 开车旅行(倍增dp+set)

传送门 解题思路: 一道比较有趣的题,解题工作主要分为两块: ①找出k(k=0表示小A先走,k=1表示小B先走,下面同理)从点i出发下一个到达的点to[k][i]; 一开始偷懒用了vector(偷懒一时爽),由于vector的erase操作是o(n)的,这个预处理时间复杂度就彪到o(n2)了.这里改成set就可以将复杂度降到o(nlogn),用链表的话讲道理可以降到o(n)但是排序就要o(nlogn)毫无*用.具体操作就是二分再前后比较(简称瞎搞). ②两个人从点i出发走2j步走各自开车所走过的