NOIP2012DAY1T3【开车旅行】

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

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

Output Format

输出共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

对于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 互不相同。

【题解】

倍增 双向链表

首先一定要预处理在A和B在每个点上出发到达的点(可以用各种数据结构处理),推荐比较高效的双向链表(与noip2016初赛完善程序第一题一样)

然后用倍增的做法f[i][j]记录从i点出发AB轮换2^j轮后到达的地点(一轮是AB都开了一次车

g[i][j][0]i到f[i][j]中A走的路程 g[i][j][1]记录B的路程

对于第一问枚举每一个起点倍增到不能走 最后比较一下就行了

第二问就直接从给定起点做倍增就行

详见代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
#include <map>
using namespace std;
long long g[100005][22][2],a,b,l,x;
double MIN,d;
int f[100005][22],get[100005][2],rank[100005],lef[100005],rig[100005];
struct info
  {
  	long long h;
  	int num;
  }city[100005],t[10];
int i,j,k,m,n,ans,s;
bool cmp(info x,info y) {return x.h<y.h;}
bool cmp1(info x,info y) {return (x.h<y.h || (x.h==y.h&&city[rank[x.num]].h<city[rank[y.num]].h));}
int main()
  {
  	scanf("%d",&n);
  	for (i=1;i<=n;i++) scanf("%lld",&city[i].h),city[i].num=i;
    sort(city+1,city+n+1,cmp);
    for (i=1;i<=n;i++) rank[city[i].num]=i;
    for (i=1;i<=n;i++) lef[i]=i-1,rig[i]=i+1;
    city[0].h=city[n+1].h=1e9;
    for (i=1;i<=n;i++)
      {
      	k=0;
      	if (lef[rank[i]]!=0) t[++k]={abs(city[lef[rank[i]]].h-city[rank[i]].h),city[lef[rank[i]]].num};
      	if (rig[rank[i]]!=n+1) t[++k]={abs(city[rig[rank[i]]].h-city[rank[i]].h),city[rig[rank[i]]].num};
      	if (lef[lef[rank[i]]]!=0) t[++k]={abs(city[lef[lef[rank[i]]]].h-city[rank[i]].h),city[lef[lef[rank[i]]]].num};
      	if (rig[rig[rank[i]]]!=n+1) t[++k]={abs(city[rig[rig[rank[i]]]].h-city[rank[i]].h),city[rig[rig[rank[i]]]].num};
        sort(t+1,t+k+1,cmp1);
        rig[lef[rank[i]]]=rig[rank[i]];
		lef[rig[rank[i]]]=lef[rank[i]];
		if (k>0) get[i][1]=t[1].num;
		if (k>1) get[i][0]=t[2].num;
	  }
	for (i=1;i<=n;i++)
	  {
	  	j=get[i][0];k=get[j][1];
	  	if (k!=0) f[i][0]=k;else f[i][0]=i;
		if (j!=0) g[i][0][0]=abs(city[rank[i]].h-city[rank[j]].h);else g[i][0][0]=1e15;
	    if (j!=0&&k!=0) g[i][0][1]=abs(city[rank[j]].h-city[rank[k]].h);else g[i][0][1]=1e15;
	  }
	for (i=1;i<=20;i++)
	  for (j=1;j<=n;j++)
	    {
	       f[j][i]=f[f[j][i-1]][i-1];
		   if (g[j][i-1][0]==1e15||g[f[j][i-1]][i-1][0]==1e15) g[j][i][0]=1e15;
		   else g[j][i][0]=g[j][i-1][0]+g[f[j][i-1]][i-1][0];
		   if (g[j][i-1][1]==1e15||g[f[j][i-1]][i-1][1]==1e15) g[j][i][1]=1e15;
		   else g[j][i][1]=g[j][i-1][1]+g[f[j][i-1]][i-1][1];
		} 
	scanf("%d",&x);MIN=1e16;
	for (i=1;i<=n;i++)
	  {
	  	l=0;k=i;a=0;b=0;
	  	for (;l+g[k][0][0]+g[k][0][1]<=x;)
	  	  {
	  	  	for (j=0;l+g[k][j][0]+g[k][j][1]<=x;j++);j--;
	  	  	l+=g[k][j][0]+g[k][j][1];
			a+=g[k][j][0];b+=g[k][j][1];
			k=f[k][j];
		  }
		if (l+g[k][0][0]<=x) a+=g[k][0][0];
		if (b==0) d=1e15;else d=a/1.0/b;
		if (d<MIN) ans=i,MIN=d; 
	  }
	printf("%d\n",ans);
	for (scanf("%d",&m);m;m--)  
	  {
	  	scanf("%d%lld",&s,&x);
	  	l=0;k=s;a=0;b=0;
	  	for (;l+g[k][0][0]+g[k][0][1]<=x;)
	  	  {
	  	  	for (j=0;l+g[k][j][0]+g[k][j][1]<=x;j++);j--;
	  	  	l+=g[k][j][0]+g[k][j][1];
			a+=g[k][j][0];b+=g[k][j][1];
			k=f[k][j];
		  }
		if (l+g[k][0][0]<=x) a+=g[k][0][0];
	    printf("%lld %lld\n",a,b);  
	  }
  }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值