【NOIP2012】开车旅行

题目描述

\(A\) 和小 \(B\) 决定利用假期外出旅行,他们将想去的城市从 \(1\)\(N\) 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 \(i\) 的海拔高度为 \(H_i\),城市 \(i\) 和城市 \(j\)之间的距离 \(d[i,j]\) 恰好是这两个城市海拔高度之差的绝对值,即 \(d[i,j]=∣H_i−H_j∣\)

旅行过程中,小 \(A\) 和小 \(B\) 轮流开车,第一天小 \(A\) 开车,之后每天轮换一次。他们计划选择一个城市 \(S\) 作为起点,一直向东行驶,并且最多行驶 \(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\) 行驶的路程总数。

输入输出格式

输入格式:

第一行包含一个整数 \(N\),表示城市的数目。

第二行有 \(N\) 个整数,每两个整数之间用一个空格隔开,依次表示城市 \(1\) 到城市 \(N\) 的海拔高度,即 \(H_1,H_2,…,H_n\),且每个 \(H_i\) 都是不同的。

第三行包含一个整数 \(X_0\)

第四行为一个整数 \(M\),表示给定 \(M\)\(S_i\)\(X_i\)

接下来的 \(M\) 行,每行包含 \(2\) 个整数 \(S_i\)\(X_i\),表示从城市 \(S_i\) 出发,最多行驶 \(X_i\) 公里。

输出格式:

输出共 \(M+1\) 行。

第一行包含一个整数 \(S_0\),表示对于给定的 \(X_0\),从编号为 \(S_0\) 的城市出发,小 \(A\) 开车行驶的路程总数与小 \(B\) 行驶的路程总数的比值最小。

接下来的 \(M\) 行,每行包含 \(2\) 个整数,之间用一个空格隔开,依次表示在给定的 \(S_i\)\(X_i\)下小 \(A\) 行驶的里程总数和小 \(B\) 行驶的里程总数。

输入输出样例

输入样例#1:

4 
2 3 1 4 
3 
4 
1 3 
2 3 
3 3 
4 3

输出样例#1:

1 
1 1 
2 0 
0 0 
0 0 

输入样例#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

输出样例#2:

2 
3 2 
2 4 
2 1 
2 4 
5 1 
5 1 
2 1 
2 0 
0 0 
0 0

分析

方法:倍增+\(DP\)+双向链表

这道题主要难在预处理部分,需要用到倍增。

后面的第一个问题,我们直接暴力枚举每一个起点即可。

至于路程改怎么算,用\(DP\)就好了。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define il inline
#define re register
#define maxn 100007
#define mymax(a,b) a>b?a:b
#define mymin(a,b) a<b?a:b
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;

template<typename T>inline void read(T &x){
    T f=1;x=0;char c;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=x*10+(c^48);
    x*=f;
}

struct city{
    int l,r,h,id;
    bool operator < (const city &a)const{return h<a.h;}
}tr[maxn];

int n,m,x,s;
int ans,disa,disb,flag;
int pre[maxn],mina[maxn],minb[maxn];
int f[25][maxn][2],fa[25][maxn][2],fb[25][maxn][2];

bool jud(int l,int r,int i){
    if(!l) return 0;
    if(!r) return 1;
    return tr[i].h-tr[l].h<=tr[r].h-tr[i].h;
}

int find(int l,int r,int i){
    if(!l) return tr[r].id;
    if(!r) return tr[l].id;
    if(tr[i].h-tr[l].h<=tr[r].h-tr[i].h) return tr[l].id;
    else return tr[r].id;
}

void prepare(){
    int j,l,r;
    for(int i=1;i<=n;++i){
        j=pre[i],l=tr[j].l,r=tr[j].r;
        if(jud(l,r,j)) mina[i]=find(tr[l].l,r,j),minb[i]=tr[l].id;
        else mina[i]=find(l,tr[r].r,j),minb[i]=tr[r].id;
        if(l) tr[l].r=r;
        if(r) tr[r].l=l;
    }
}

void init(){
    for(int i=1;i<=n;++i){
        if(mina[i]) f[0][i][0]=mina[i],fa[0][i][0]=abs(tr[pre[i]].h-tr[pre[mina[i]]].h);
        if(minb[i]) f[0][i][1]=minb[i],fb[0][i][1]=abs(tr[pre[i]].h-tr[pre[minb[i]]].h);
        fa[0][i][1]=fb[0][i][0]=0;
    }
    for(int i=1;i<=20;++i)
        for(int j=1;j<=n;++j)
            for(int k=0;k<2;++k){
                if(i==1) flag=k^1;
                else flag=k;
                if(f[i-1][j][k]) f[i][j][k]=f[i-1][f[i-1][j][k]][flag];
                if(f[i][j][k]) {
                    fa[i][j][k]=fa[i-1][j][k]+fa[i-1][f[i-1][j][k]][flag];
                    fb[i][j][k]=fb[i-1][j][k]+fb[i-1][f[i-1][j][k]][flag];
                }
            }
}

void solve(int xi,int pos){
    disa=disb=flag=0;
    for(int i=20;i>=0;--i)
        if(f[i][pos][flag]&&disa+disb+fa[i][pos][flag]+fb[i][pos][flag]<=xi){
            disa+=fa[i][pos][flag],disb+=fb[i][pos][flag];
            if(!i) flag^=1;
            pos=f[i][pos][flag];
        }
}

int main(){
    read(n);
    for(int i=1;i<=n;++i) read(tr[i].h),tr[i].id=i;
    sort(tr+1,tr+1+n);
    for(int i=1;i<=n;++i) pre[tr[i].id]=i,tr[i].l=i-1,tr[i].r=i+1;
    tr[n].r=0;
    prepare();
    init();
    read(x),read(m);
    double minn=1000000001.0;
    for(int i=1;i<=n;++i){
        solve(x,i);
        if(disb&&1.0*disa/disb<minn){
            minn=1.0*disa/disb;
            ans=i;
        }
    }
    printf("%d\n",ans);
    while(m--){
        read(s),read(x);
        solve(x,s);
        printf("%d %d\n",disa,disb);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hlw1/p/11261143.html