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步走各自开车所走过的路程分别表示为da[i][j][k],db[i][j][k],最终达到点表示为f[i][j][k]。da[i][j][k]表示k从i点出发走了2j步的小A要开车的路程。那么da[i][j][k]+db[i][j][k]就表示了k先手开车从点i出发走了2j步共走过的路程。预处理出第一步和第二步,(转移方程见代码)。

  运用二进制拆分的思想,假设出发点为s,p表示当前到达的点,ta、tb分别表示小A和小B从s出发达到p点各自走过的路程,x表示能走得最大路程。从log(最大步数)到0遍历j,如果ta+tb+da[p][j][k]+db[p][j][k]<=x,则令ta和tb分别加上da[p][j][k]和db[p][j][k],p=f[i][j][k]。枚举所有的点进行上述操作就可得到结果。

丑陋的代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll da[maxn][32][2],db[maxn][32][2];map <int,int> m;
int h[maxn],to[2][maxn],f[maxn][32][2];set <int> v;
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&h[i]),v.insert(h[i]),m[h[i]]=i;
    for(int i=1;i<n;i++){
        v.erase(h[i]);
        auto id=v.lower_bound(h[i]);auto id1=id,id2=id;
        if(id==v.begin()){
            to[0][i]=m[(*id)];
            id1++;
            if(id1!=v.end())
                to[1][i]=m[(*id1)];
        }
        else if(id==v.end()){
            id1--;
            if(id!=v.begin())
                to[0][i]=m[(*id1)];
            if(id1!=v.begin())
                id1--,to[1][i]=m[(*id1)];
        }
        else{
            id1--;
            int t1=abs(h[i]-(*id)),t2=abs(h[i]-(*id1));
            if(t1>t2){
                to[0][i]=m[(*id1)];id2=id1;id2--;
                int t3=(id1!=v.begin())?abs(h[i]-(*id2)):2e9;
                if(t1>=t3)        to[1][i]=m[(*id2)];
                else if(t1<t3)    to[1][i]=m[(*id)];
            }
            else if(t1<t2){
                to[0][i]=m[(*id)];id2++;
                int t3=(id2!=v.end())?abs(h[i]-(*id2)):2e9;
                if(t2>t3)        to[1][i]=m[(*id2)];
                else if(t2<=t3)    to[1][i]=m[(*id1)];
            }
            else{
                to[0][i]=m[(*id1)];
                to[1][i]=m[(*id)];
            }
        }
    }
    for(int i=1;i<=n;i++)
        f[i][0][0]=to[1][i],f[i][0][1]=to[0][i];
    for(int i=1;i<=n;i++)
        for(int k=0;k<=1;k++)
            f[i][1][k]=f[f[i][0][k]][0][1-k];
    for(int j=2;j<=20;j++)
        for(int i=1;i<=n;i++)
            for(int k=0;k<=1;k++)
                f[i][j][k]=f[f[i][j-1][k]][j-1][k];
    for(int i=1;i<=n;i++)
        da[i][0][0]=abs(h[(to[1][i]==0)?i:to[1][i]]-h[i]),
        db[i][0][1]=abs(h[(to[0][i]==0)?i:to[0][i]]-h[i]);
    for(int i=1;i<=n;i++)
        for(int k=0;k<=1;k++)
            da[i][1][k]=da[i][0][k]+da[f[i][0][k]][0][1-k],
            db[i][1][k]=db[i][0][k]+db[f[i][0][k]][0][1-k];
    for(int j=2;j<=20;j++)
        for(int i=1;i<=n;i++)
            for(int k=0;k<=1;k++)
                da[i][j][k]=da[i][j-1][k]+da[f[i][j-1][k]][j-1][k],
                db[i][j][k]=db[i][j-1][k]+db[f[i][j-1][k]][j-1][k];
    int x;scanf("%d",&x);int ans;double mi=1e18;
    for(int s=1;s<n;s++){
        int p=s;ll ta=0,tb=0;
        for(int j=20;j>=0;j--)
            if(ta+tb+da[p][j][0]+db[p][j][0]<=x){
                ta+=da[p][j][0];
                tb+=db[p][j][0];
                p=f[p][j][0];
            }
        double t;if(tb==0)    t=1e18-1;
        else    t=(double)1.0*ta/tb;
        if(t<mi)    ans=s,mi=t;
    }
    printf("%d\n",ans);
    int q;scanf("%d",&q);
    while(q--){
        int s;scanf("%d%d",&s,&x);
        int p=s;ll ta=0,tb=0;
        for(int j=20;j>=0;j--)
            if(ta+tb+da[p][j][0]+db[p][j][0]<=x){
                ta+=da[p][j][0];
                tb+=db[p][j][0];
                p=f[p][j][0];
            }
        printf("%d %d\n",ta,tb);
    }
}

原文地址:https://www.cnblogs.com/r138155/p/12693344.html

时间: 2024-10-11 03:29:53

CH5701 开车旅行(倍增dp+set)的相关文章

【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

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

开车旅行(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总是

noip2012 开车旅行

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

codevs 1199 开车旅行 2012年NOIP全国联赛提高组

1199 开车旅行 2012年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 小A 和小B决定利用假期外出旅行,他们将想去的城市从1到N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i的海拔高度为Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i, j] = |Hi − Hj|. 旅行过程中,小A 和小B轮流开

2012Noip提高组Day1 T3 开车旅行

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

开车旅行

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