您的位置:首页 > 其它

【NOIP2012】Day1T3 开车旅行

2016-10-30 15:07 330 查看

开车旅行

Description

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

旅行过程中,小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%的数据,有1≤N≤20,1≤M≤20;

对于40%的数据,有1≤N≤100,1≤M≤100;

对于50%的数据,有1≤N≤100,1≤M≤1,000;

对于70%的数据,有1≤N≤1,000,1≤M≤10,000;

对于100%的数据,有1≤N≤100,000,1≤M≤10,000,−1,000,000,000≤Hi≤1,000,000,000,0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,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
;
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
,a
,b
,w
,g
[18];
LL f
[18][2],ansa,ansb,v
;
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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: