3101. 【NOIP2012提高组】开车旅行

Description

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

输入文件为drive.in。 

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

第二行有 N 个整数,每两个整数之间用一个空格隔开,依次表示城市 1 到城市 N 的海拔高度,即H1,H2,……,Hn,且每个Hi都是不同的。 

第三行包含一个整数 X0。 

第四行为一个整数 M,表示给定M组Si和 Xi。 

接下来的M行,每行包含2个整数Si和Xi,表示从城市 Si出发,最多行驶Xi公里。 

 

 

 

Output

  输出文件为drive.out。 

输出共M+1 行。 

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

接下来的 M 行,每行包含 2 个整数,之间用一个空格隔开,依次表示在给定的 Si和

Xi下小A行驶的里程总数和小B 行驶的里程总数。 

3

 

Sample Input

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
2 0
0 0
0 0

【输入输出样例 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】 
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0
【输入输出样例 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。
如果从城市 4 出发,则路线为 4 -> 6 -> 7,小 A 和小 B 走的距离分别为 2,4。
如果从城市 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。 

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

 

Solution 

语文阅读理解题+数学思维逻辑题

70%:显然的n^2暴力。n^2预处理出每个点第一近和第二近的点。对于第一问,枚举出发点,然后走,对于第二问,直接走就可以了。时间复杂度O(n^2+nm)。

100%:首先预处理就爆掉了,我们想办法优化,方法有很多种,如平衡树,线段树,双向链表。这里给出双向链表的方法:我们发现离它最近的一个点肯定是第一比它小的点或第一比它大的点,第二个的类似,所以可以把所有的高度排一遍序,记录下原来的位置现在所对应的,用一个双向链表把这个排序后的序列存起来。然后从前往后扫一遍原序列,对于每个原数组中的位置,我们找到(记录)它对应的在排完序后的位置,那么前一个元素就是第一个比它小的数,后一个就是第一个比它大的数,比较它们两个就能找出离它第一近的数,第二近类似,即在比离它第一近的数小的或大的第一个数(除它本身以外),每次做完一个以后我们就把它的位置删除(双向链表的删除),因为我们是从前往后扫的,所以就能保证找到的第一近和第二近一定是在它后面。以上就是这题的重点了。之后我们考虑优化旅行的时间,其实旅行一次也可以不用O(n),如果这样时间就会炸掉,通过观察数据可得,O(n log n)的时间是可以过的。所以我们就考虑O(log n)的时间旅行一次,所以就可以用倍增。我们设A走一次和B走一次为一,f[i][j]表示从第i个点走2^j走到的点,disa[i][j]表示A从第i开始走2^j走过的路程,disb[i][j]表示B从i开始走2^j走过的路程。然后能走就走,边走边记录A和B所走的路程和即可。由于AB一起走视为一,所以走到时候是算disa和disb的和是否大于能行驶的路程,小于就走,大于就不走。但是还有一种A自己一个人多走一的情况,最后要判断一下。

为什么说这是一道语文理解题呢?因为它有很多坑。

1.本题中如果当前城市到两个城市的距离 相同,则认为离海拔的那个城市更近

2.如果从多个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比 值都最小,则输出海拔最高的那个城市。

3.如果小 B 的行驶路程为 0,此时的比值可视为无穷大且两个无穷大视为相等(两个无穷大则相等我至今还不懂,幸好没用)

 

70%Code

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define inf 2147483647
#define ll long long
#define ld long double
#define N 100010
using namespace std;
ll n,m,x0,h[N],dis1[N],dis2[N],k1[N],k2[N],x[N],s[N],hans,aa,bb;
ld ans=(ld)inf;
struct node{ll h,id;}a[N];
bool cmp(node x,node y){return x.h<y.h;}
void work(ll x,ll l){
	ll s=0,p=1;aa=0,bb=0;
	while(x){
		if(p){
			ll dis=abs(dis2[x]-h[x]);
			if(s+dis>l) break;
			s+=dis,x=k2[x];aa+=dis;
		}
		else{
			ll dis=abs(dis1[x]-h[x]);
			if(s+dis>l) break;
			s+=dis,x=k1[x];bb+=dis;
		}
		p=1-p;
	}
}
int main(){
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&h[i]),a[i].h=h[i],a[i].id=i;
	memset(dis1,127,sizeof(dis1));
	memset(dis2,127,sizeof(dis2));
	for(int i=1;i<=n-1;i++){
		for(int j=i+1;j<=n;j++){
			ll xx=abs(h[i]-h[j]),yy=abs(dis1[i]-h[i]),zz=abs(dis2[i]-h[i]);
			if(xx<yy||(xx==yy&&dis1[i]>h[j])){
				dis2[i]=dis1[i],k2[i]=k1[i];
				dis1[i]=h[j],k1[i]=j;
			}
			else if(xx<zz||(xx==zz&&dis2[i]>h[j])){dis2[i]=h[j],k2[i]=j;}
		}
	}
	scanf("%lld",&x0);
	for(int i=1;i<=n;i++){
		work(i,x0);
		if(bb&&(ans>(ld)aa/bb||(ans==(ld)aa/bb&&h[hans]<h[i]))){
			ans=(ld)aa/bb,hans=i;
		}
	}
	printf("%lld\n",hans);
	scanf("%lld",&m);
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&s[i],&x[i]);
		work(s[i],x[i]);
		printf("%lld %lld\n",aa,bb);
	}
	return 0;
}

 

100%Code 

#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 2147483647
#define ld long double 
#define ll long long
#define N 100010
using namespace std;
int n,m,x,sa,sb,hs,st,nx[N],ls[N],a[N],b[N],c[N],s[N],f[N][19];
ll pra[N][19],prb[N][19];
struct node{int v,id;}h[N];
ld ans=(ld)inf,t;
bool cmp(node x,node y){return x.v<y.v;}
int dis(int x,int y){return abs(h[x].v-h[y].v);}
void get(int x){
	b[x]=(ls[x]&&(dis(x,ls[x])<=dis(x,nx[x])||!nx[x]))?ls[x]:nx[x];
	if(b[x]==ls[x]){
		a[x]=(ls[b[x]]&&(dis(x,ls[b[x]])<=dis(x,nx[x])||!nx[x]))?ls[b[x]]:nx[x];}
	else{
		a[x]=(ls[x]&&(dis(x,ls[x])<=dis(x,nx[b[x]])||!nx[b[x]]))?ls[x]:nx[b[x]];
	}
	nx[ls[x]]=nx[x];ls[nx[x]]=ls[x];
}
void work(int s,int x){
	sa=sb=0;
	for(int i=18;i>=0;i--){
		if(x>=pra[s][i]+prb[s][i]){
			x-=pra[s][i];x-=prb[s][i];
			sa+=pra[s][i];sb+=prb[s][i];
			s=f[s][i];
		}
	}
	if(x>=pra[s][0]){x-=pra[s][0];sa+=pra[s][0];}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&h[i].v),h[i].id=i,s[i]=h[i].v;
	sort(h+1,h+1+n,cmp);
	for(int i=1;i<=n;i++){c[h[i].id]=i;ls[i]=i-1;nx[i]=i+1;}
	nx[n]=0;
	for(int i=1;i<=n;i++) get(c[i]);
	for(int i=1;i<=n;i++){
		int x=c[i];
		if(b[a[x]]&&a[x])prb[i][0]=abs(h[a[x]].v-h[b[a[x]]].v);
		if(a[x]) pra[i][0]=abs(h[x].v-h[a[x]].v);
		f[i][0]=h[b[a[x]]].id;
	}
	for(int j=1;j<=18;j++){
		for(int i=1;i<=n;i++){
			f[i][j]=f[f[i][j-1]][j-1];
			pra[i][j]=pra[i][j-1]+pra[f[i][j-1]][j-1];
			prb[i][j]=prb[i][j-1]+prb[f[i][j-1]][j-1];
		}
	}
	scanf("%d",&x);
	for(int i=1;i<=n;i++){
		work(i,x);
		if(!sb) continue;else t=(ld)sa/sb;
		if(ans>t||(ans==t&&s[i]>s[hs])){ans=t;hs=i;}
	}
	printf("%d\n",hs);
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&st,&x);
		work(st,x);
		printf("%d %d\n",sa,sb);
	}
	return 0;
}

(我的码量目前是最短的了......) 



作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.youkuaiyun.com/zsjzliziyang/article/details/86556941

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值