P1081 开车旅行

题意:

小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 11 到 NN 编号,且编号较小的城市在编号较大的城市的西边,

已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 $H_i$​ ,城市 i 和城市 j 之间的距离 $d_{[i,j]}=|H_i-H_j|$ 。

旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次。

他们计划选择一个城市 SS 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行。

小 A 和小 B 的驾驶风格不同,小 B 总是沿着前进方向选择一个最近的城市作为目的地,而小 A 总是沿着前进方向选择第二近的城市作为目的地

(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。

如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 X 公里,他们就会结束旅行。

在启程之前,小 A 想知道两个问题:

1:对于一个给定的 $X=X_0$​ ,从哪一个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值最小(如果小 B 的行驶路程为 0 ,此时的比值可视为无穷大,且两个无穷大视为相等)。

  如果从多个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值都最小,则输出海拔最高的那个城市。

2:对任意给定的 $X=X_i$​ 和出发城市 $S_i$​ ,小 A 开车行驶的路程总数以及小 B 行驶的路程总数。

以f[i][j]记录从i点经过$2^j$轮后他们所到的点

以disa[i][j]记录a从i点经过$2^j$天自己走的距离

以disb[i][j]记录b从i点经过$2^j$天自己走的距离

以ato[i]记录a从i点走一步所到的点

以bto[i]记录b从i点走一步所到的点

首先,把所有城市按海拔排序,并建立排序前后的映射

从第一个城市开始枚举,那么所有的城市都在它的东边

然后对应到排序好的城市中,处理出ato[i],bto[i]

处理完把当前城市删除,第二个变成第一个,因此再次求的时候仍然所有点都在东边

之后处理st表,模拟即可

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define int long long
#define olinr return
#define _ 0
#define nmr 105050
#define love_nmr 0
#define DB double
int n;
struct node
{
    int pre;
    int nxt;
    int h;
    int id;
    friend bool operator < (const node &a,const node &b)
    {
        return a.h<b.h;
    }
}s[nmr];
int disa[nmr][31];
int disb[nmr][31];
int f[nmr][31];
int ato[nmr];
int bto[nmr];
int X;
int m;
int mp[nmr];
int ans;
DB minn=0x7fffffff;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            f=-f;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline void put(int x)
{
    if(x<0)
    {
        x=-x;
        putchar('-');
    }
    if(x>9)
        put(x/10);
    putchar(x%10+'0');
}
inline int abss(int x)
{
    if(x<0) return -x;
    return x;
} 
inline bool near_left(int now,int l,int r)
{
    if(!l) return false;
    if(!r) return true;
    int disl=abss(s[l].h-s[now].h);
    int disr=abss(s[r].h-s[now].h);
    if(disl<disr) return true;
    if(disl>disr) return false;
    return s[l].h<s[r].h;
}
inline int opposite(int now,int l,int r)
{
    if(!l) return s[r].id;
    if(!r) return s[l].id;
    int disl=abss(s[l].h-s[now].h);
    int disr=abss(s[r].h-s[now].h);
    if(disl<disr) return s[l].id;
    if(disl>disr) return s[r].id;
    if(s[l].h<s[r].h) return s[l].id;
    return s[r].id;
}
inline void beizeng()
{
    for(int i=1;i<=n;i++)   //a---->b---->c
    {
        f[i][0]=bto[ato[i]];   //a---->c
        disa[i][0]=abss(s[mp[i]].h-s[mp[ato[i]]].h);   //a---->b
        disb[i][0]=abss(s[mp[f[i][0]]].h-s[mp[ato[i]]].h);  //b---->c
    }
    for(int j=1;j<=23;j++)
        for(int i=1;i<=n;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            disa[i][j]=disa[i][j-1]+disa[f[i][j-1]][j-1];
            disb[i][j]=disb[i][j-1]+disb[f[i][j-1]][j-1];
        }
}
inline void running(int sta,int &a,int &b,int limit)
{
    for(int i=22;i>=0;i--)
    {
        if(f[sta][i]&&(a+b+disa[sta][i]+disb[sta][i])<=limit)  //模拟倍增过程,A、B一起跑
        {
            a+=disa[sta][i];
            b+=disb[sta][i];
            sta=f[sta][i];
        }
    }
    if(ato[sta]&&(a+b+disa[sta][0])<=limit) //现在AB同时跑不行,但可以看看A单独跑行不行!!
        a+=disa[sta][0];
}
signed main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        s[i].h=read();
        s[i].id=i;
    }
    sort(s+1,s+n+1);
    for(int i=1;i<=n;i++)
    {
        s[i].pre=i-1;
        s[i].nxt=i+1;
        mp[s[i].id]=i;       //排序前后映射
    }
    s[n].nxt=s[1].pre=0;      //排序后的左右
    for(int i=1;i<=n;i++)
    {
        int pos=mp[i];
        int l=s[pos].pre;      //此时l,r都在当前的东边!
        int r=s[pos].nxt;        
        if(near_left(pos,l,r))    //判断左边是否最近
        {
            bto[i]=s[l].id;
            ato[i]=opposite(pos,s[l].pre,r);   //若左边近,用左边的左边和右边比次近
        }
        else
        {
            bto[i]=s[r].id;
            ato[i]=opposite(pos,l,s[r].nxt);
        }
        if(r) s[r].pre=l;    //用完把当前删掉,保证下一个是最西边的
        if(l) s[l].nxt=r;
    }
    beizeng();   //倍增预处理
    X=read();
    m=read();
    for(int i=1;i<=n;i++)
    {
        int tota=0;
        int totb=0;
        running(i,tota,totb,X);
        if(totb&&(((DB)tota/(DB)totb)<minn))
        {
            minn=(DB)tota/(DB)totb;
            ans=i;
        }
    }
    if(ans==0) ans=s[n].id;
    put(ans);
    putchar('
');
    for(int i=1;i<=m;i++)
    {
        int sta,limit;
        sta=read();
        limit=read();
        int tota=0;
        int totb=0;
        running(sta,tota,totb,limit);
        put(tota);
        putchar(' ');
        put(totb);
        putchar('
');
    }
    olinr ~~(0^_^0)+love_nmr;
}
原文地址:https://www.cnblogs.com/olinr/p/9535528.html