ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LG1081 开车旅行

2019-03-30 10:53:58  阅读:250  来源: 互联网

标签:旅行 开车 LG1081 int 城市 距离 leq ans sumb


题意

城市\(i\)的海拔高度为\(H_i\)(各不相同)。定义距离为海拔差的绝对值
小\(A\)和小\(B\)轮流开车。从\(S\)起,一直向东行驶。
小\(A\)会选择第二近的城市作为目的地。小\(B\)选择一个最近的城市作为目的地。(如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果无法再开了,或者到达目的地会使行驶的总距离超出\(X\)公里,他们就会结束旅行。

单次询问:给定\(X\),问从哪个城市出发,小\(A\)行的路程与小\(B\)行的路程的比值最小
多次询问:给定\(S\)和\(X\),问小\(A\)行的路程与小\(B\)行的路程
\(1 \leq N \leq 10^5,1 \leq M \leq 10^5\)

思路

首先肯定要解决一个问题,小\(A\)和小\(B\)的目的地在哪里。
先排序,然后从第一个城市开始,比较\(i-1,i-2,i+1,i+2\)处距离。
很显然,由于是第一个点,这个时候找到的任何点一定在它东边。
同样的,找完第一个点后将第一个点删除,那么第二个点自然成为了第一个点,依次下去,便在\(O(n)\)的复杂度内完成了预处理。

接下来,就可以想到用倍增来解决。
用\(f[i][j]\)表示从\(j\)出发第\(2^i\)天到达的城市,发现除了\(i=0\)时是\(A\)在开其他都是\(B\)开,所以转移就很简单了\(f[i][j]=f[i-1][f[i-1][j]];\)
那么求出了路径,距离只要减一减就好了。
用\(g[i][j][0]\)表示从\(j\)出发第\(2^i\)天\(A\)开的,用\(g[i][j][1]\)表示从\(j\)出发第\(2^i\)天\(B\)开的,转移:
\(g[i][j][0]=g[i-1][j][0]+g[i-1][f[i-1][j]][0];\)
\(g[i][j][1]=g[i-1][j][1]+g[i-1][f[i-1][j]][1];\)

接下来的询问只要每次倍增凑距离就好了
对于第一个询问枚举出发点再比较
时间复杂度\(O((n+m)logn)\)

#include <bits/stdc++.h>
const int N=100005;
double INF=1000000000;
using std::sort;
int a[N],b[N],Next[N],last[N],c[N],n,m,f[21][N],x,ans=0,s;
long long h[N],suma,sumb,g[21][N][2];
bool cmp(int x,int y){
    return h[x]<h[y];
}
int d(int x,int y){
    return abs(h[x]-h[y]);
}
void solve(int x,int s){
    for (int i=20;i>=0;i--)
        if (g[i][s][0]+g[i][s][1]<=1ll*x){
            x-=g[i][s][0]+g[i][s][1];
            suma+=g[i][s][0];
            sumb+=g[i][s][1];
            if (s==f[i][s]) return;
            s=f[i][s];
        }
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&h[i]),c[i]=i;
    h[0]=-200000000000;
    sort(c+1,c+n+1,cmp);
    for (int i=1;i<=n;i++) last[c[i]]=c[i-1],Next[c[i]]=c[i+1];
    for (int i=1;i<=n;i++){
        int x1=last[i],x2=last[x1],x3=Next[i],x4=Next[x3];
        int dis1=abs(h[i]-h[x1]),dis2=abs(h[i]-h[x2]),dis3=abs(h[i]-h[x3]),dis4=abs(h[i]-h[x4]);
        if (dis1<dis3 ||(dis1==dis3 && h[x1]<h[x3])) b[i]=x1; else b[i]=x3;
        if (dis2<dis3 ||(dis2==dis3 && h[x2]<h[x3])) a[i]=x2;
        else if (dis4<dis1 ||(dis4==dis1 && h[x4]<h[x1])) a[i]=x4;
        else if (dis1<dis3 ||(dis1==dis3 && h[x1]<h[x3])) a[i]=x3;
        else a[i]=x1;
        if (a[i]==0) a[i]=i;
        if (b[i]==0) b[i]=i;
        last[Next[i]]=last[i],Next[last[i]]=Next[i];
    }
    for (int i=1;i<=n;i++){
        f[0][i]=a[i];if (a[i]!=i) f[1][i]=b[a[i]];else f[1][i]=i;
        g[0][i][0]=d(i,f[0][i]),g[1][i][0]=g[0][i][0];
        if (a[i]!=i)g[1][i][1]=d(a[i],b[a[i]]);
    }
    for (int i=2;i<=20;i++)
        for (int j=1;j<=n;j++){
            f[i][j]=f[i-1][f[i-1][j]];
            g[i][j][0]=g[i-1][j][0]+g[i-1][f[i-1][j]][0];
            g[i][j][1]=g[i-1][j][1]+g[i-1][f[i-1][j]][1];
        }
    scanf("%d",&x);
    double now=INF;
    for (int i=1;i<=n-2;i++){
        suma=0,sumb=0;
        solve(x,i);
        if (now==INF && sumb==0 && h[i]>h[ans]) ans=i;
        if (sumb==0) continue;
        if (suma*1.0/sumb<now ||(suma*1.0/sumb==now && h[i]>h[ans])){
            now=suma*1.0/sumb;
            ans=i;
        } 
    }
    printf("%d\n",ans);
    scanf("%d",&m);
    for (int i=1;i<=m;i++){
        scanf("%d%d",&s,&x);
        suma=0,sumb=0;
        solve(x,s);
        printf("%lld %lld\n",suma,sumb);
    }
} 

后记

要注意\(h[0]\)的初始化,原来没仔细看数据结果爆了\(4\)发

标签:旅行,开车,LG1081,int,城市,距离,leq,ans,sumb
来源: https://www.cnblogs.com/flyfeather6/p/10625564.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有