【NOIP2012】Day1T3 开车旅行

开车旅行


  • Description

小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi,城市 i 和城市j 之间的距离 d[i,j] 恰好是这两个城市海拔高度之差的绝对值,即 d[i,j]=HiHj
旅行过程中,小A和小B轮流开车,第一天小A开车,之后每天轮换一次。他们计划选择一个城市S作为起点,一直向东行驶,并且最多行驶X公里就结束旅行。小A和小B的驾驶风格不同,小B总是沿着前进方向选择一个最近的城市作为目的地,而小A总是沿着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出X公里,他们就会结束旅行。
在启程之前,小A想知道两个问题:
1. 对于一个给定的 X =X0,从哪一个城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值最小(如果小B的行驶路程为0,此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
2. 对任意给定的 X =Xi 和出发城市 Si ,小A开车行驶的路程总数以及小B行驶的路程总数。

  • Input Format

输入文件为drive.in。
第一行包含一个整数N,表示城市的数目。
第二行有N个整数,每两个整数之间用一个空格隔开,依次表示城市1到城市N的海拔高度,即 H1,H2,Hn ,且每个 Hi 都是不同的。
第三行包含一个整数 X0
第四行为一个整数M,表示给定M组 Si Xi
接下来的M行,每行包含2个整数 Si Xi ,表示从城市 Si 出发,最多行驶 Xi 公里。

  • Output Format

输出文件为drive.out。
输出共M+1行。
第一行包含一个整数 S0 ,表示对于给定的 X0 ,从编号为 S0 的城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值最小。
接下来的M行,每行包含2个整数,之间用一个空格隔开,依次表示在给定的 Si Xi 下小A行驶的里程总数和小B行驶的里程总数。

  • Sample Input

样例1
4
2 3 1 4
3
4
1 3
2 3
3 3
4 3
样例2
10
4 5 6 1 2 3 7 8 9 10
7
10
1 7
2 7
3 7
4 7
5 7
6 7
7 7
8 7
9 7
10 7

  • Sample Output

样例1
1
1 1
2 0
0 0
0 0
样例2
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0

  • Hint

【输入输出样例1说明】
这里写图片描述
各个城市的海拔高度以及两个城市间的距离如上图所示。
如果从城市1出发,可以到达的城市为2,3,4,这几个城市与城市1的距离分别为1,1,2,但是由于城市3的海拔高度低于城市2,所以我们认为城市3离城市1最近,城市2离城市1第二近,所以小A会走到城市2。到达城市2后,前面可以到达的城市为3,4,这两个城市与城市2的距离分别为2,1,所以城市4离城市2最近,因此小B会走到城市4。到达城市4后,前面已没有可到达的城市,所以旅行结束。
如果从城市2出发,可以到达的城市为3,4,这两个城市与城市2的距离分别为2,1,由于城市3离城市2第二近,所以小A会走到城市3。到达城市3后,前面尚未旅行的城市为4,所以城市4离城市3最近,但是如果要到达城市4,则总路程为2+3=5>3,所以小B会直接在城市3结束旅行。
如果从城市3出发,可以到达的城市为4,由于没有离城市3第二近的城市,因此旅行还未开始就结束了。
如果从城市4出发,没有可以到达的城市,因此旅行还未开始就结束了。

【输入输出样例2说明】
当X=7时,
如果从城市1出发,则路线为1 -> 2 -> 3 -> 8 -> 9,小A走的距离为1+2=3,小B走的距离为1+1=2。(在城市1时,距离小A最近的城市是2和6,但是城市2的海拔更高,视为与城市1第二近的城市,所以小A最终选择城市2;走到9后,小A只有城市10可以走,没有第2选择可以选,所以没法做出选择,结束旅行)
如果从城市2出发,则路线为2 -> 6 -> 7,小A和小B走的距离分别为2,4。
如果从城市3出发,则路线为3 -> 8 -> 9,小A和小B走的距离分别为2,1。
如果从城市5出发,则路线为5 -> 7 -> 8,小A和小B走的距离分别为5,1。
如果从城市6出发,则路线为6 -> 8 -> 9,小A和小B走的距离分别为5,1。
如果从城市7出发,则路线为7 -> 9 -> 10,小A和小B走的距离分别为2,1。
如果从城市8出发,则路线为8 -> 10,小A和小B走的距离分别为2,0。
如果从城市9出发,则路线为9,小A和小B走的距离分别为0,0(旅行一开始就结束了)。
如果从城市10出发,则路线为10,小A和小B走的距离分别为0,0。
从城市2或者城市4出发小A行驶的路程总数与小B行驶的路程总数的比值都最小,但是城市2的海拔更高,所以输出第一行为2。

【数据范围】
对于30%的数据,有 1N201M20
对于40%的数据,有 1N1001M100
对于50%的数据,有 1N1001M1,000
对于70%的数据,有 1N1,0001M10,000
对于100%的数据,有 1N100,0001M10,0001,000,000,000Hi1,000,000,0000X01,000,000,0001SiN0Xi1,000,000,000 ,数据保证 Hi 互不相同。


  • 分析

题意明了,思路清晰,就是维护一下每个城市的最短和次短城市,然后直接做就可以了。在维护最短和次短时,可以用双向链表(我使用线段树)。前进的时候需要用到倍增的思想,跳跃式前进。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define N 100005
#define LL long long
using namespace std;
struct Data{int d;LL v;}p[N];
struct note{int max,min;}t[N*5];
inline bool cmp(const Data x,const Data y){return x.v<y.v||x.v==y.v&&x.d<y.d;}
double sum,ans;
int n,m,tot,x,y,k;
int h[N],a[N],b[N],w[N],g[N][18];
LL f[N][18][2],ansa,ansb,v[N];
void change(int v,int l,int r,int x){
    if (l==r){t[v].max=t[v].min=l;return;}
    int m=(l+r)/2;
    if (x<=m) change(v*2,l,m,x);
    else change(v*2+1,m+1,r,x);
    t[v].max=max(t[v*2].max,t[v*2+1].max);
    t[v].min=min(t[v*2].min,t[v*2+1].min);
}
int Getmax(int v,int l,int r,int x,int y){
    if (x>y) return 0;
    if (l==x && r==y) return t[v].max;
    int mid=(l+r)/2;
    if (y<=mid) return Getmax(v*2,l,mid,x,y);
    if (x>=mid+1) return Getmax(v*2+1,mid+1,r,x,y);
    return max(Getmax(v*2,l,mid,x,mid),Getmax(v*2+1,mid+1,r,mid+1,y));
}
int Getmin(int v,int l,int r,int x,int y){
    if (x>y) return n+1;
    if (l==x && r==y) return t[v].min;
    int mid=(l+r)/2;
    if (y<=mid) return Getmin(v*2,l,mid,x,y);
    if (x>=mid+1) return Getmin(v*2+1,mid+1,r,x,y);
    return min(Getmin(v*2,l,mid,x,mid),Getmin(v*2+1,mid+1,r,mid+1,y));
}
void solve(int x,int y){
    ansa=ansb=0;
    for (int i=17;i>=0;i--) 
        if (f[x][i][0]+f[x][i][1]<=y){
            y-=f[x][i][0]+f[x][i][1];
            ansa+=f[x][i][0];ansb+=f[x][i][1];
            x=g[x][i];
        } 
    if (f[x][0][0]<=y) ansa+=f[x][0][0];
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&v[i]),p[i].v=v[i],p[i].d=i;
    sort(p+1,p+n+1,cmp); v[0]=1<<31;
    for (int i=1;i<=n;i++) h[p[i].d]=++tot,w[tot]=p[i].d;
    for (int i=1;i<=5*n;i++) t[i].min=n+1;
    for (int i=n;i;i--){
        p[1].d=Getmin(1,1,n,h[i]+1,n); p[2].d=Getmax(1,1,n,1,h[i]-1);
        p[3].d=Getmin(1,1,n,p[1].d+1,n); p[4].d=Getmax(1,1,n,1,p[2].d-1);
        for(int j=1;j<=4;j++) p[j].v=abs(v[i]-v[w[p[j].d]]);    
        sort(p+1,p+5,cmp);
        if (p[1].d!=0&&p[1].d!=n+1) b[i]=w[p[1].d];
        if (p[2].d!=0&&p[2].d!=n+1) a[i]=w[p[2].d];
        change(1,1,n,h[i]);         
    }
    for (int i=1;i<=n;i++){
        g[i][0]=b[a[i]];
        f[i][0][0]=abs(v[i]-v[a[i]]);
        f[i][0][1]=abs(v[a[i]]-v[b[a[i]]]);
    }
    for (int j=1;j<=17;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];
            f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
        }
    scanf("%d",&x); ans=1<<30;
    for (int i=1;i<=n;i++){
        solve(i,x);
        if (!ansb) sum=1<<30;
        else sum=ansa*1.0/ansb;
        if (sum<ans || (sum==ans && v[i]>v[k])) ans=sum,k=i;
    }
    printf("%d\n",k);
    for(scanf("%d",&m);m;m--) {
        scanf("%d%d",&x,&y);
        solve(x,y);
        printf("%lld %lld\n",ansa,ansb);  
    }
    fclose(stdin); fclose(stdout);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值