P1081 [NOIP 2012 提高组] 开车旅行 题解

传送门

前言

爆肝到半夜,中间假了一次,最终调过了两个样例,交上去过了。

题解

思路

首先进行预处理。
用一种你喜欢的数据结构维护每个城市的海拔,容易求出从每个城市出发,小 \(A\) 和小 \(B\) 开一天能到达的城市。
因为小 \(A\) 开一天,小 \(B\) 开一天,所以设每两天为一个周期。用一个倍增数组 \(f_{i,j}\) 维护从第 \(i\) 个城市出发,行驶 \(2^j\) 个周期,能到达的城市,用数组维护这段路程的长度、小 \(A\) 开的路程和小 \(B\) 开的路程。
对于第一个问题,我们枚举每一个城市,通过求出从这里出发所能到达最远的城市,即可得到小 \(A\) 和小 \(B\) 分别行驶的路程,就能在 \(O(n\log{n})\) 的时间内得到答案。
对于第二个问题,我们仿照上一问直接求解,时间复杂度 \(O(m\log{n})\)

实现

关于预处理,由于海拔各不相同,所以可选的数据结构较多。笔者使用 vector,每次用 lower_bound 查找,倒着扫一遍就出来了。
因为要求第二近的,所以在需要在 vector 中遍历每个城市前后两个城市的海拔。
关于求解,需要注意,可能真正最远的城市不一定是通过 \(f_{i,j}\) 数组求出的城市,需要判断能不能让小 \(A\) 再走一天。
剩下的细节看代码。

代码

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define N 100005
#define abs(x) ((x)>0?(x):-(x))
int n,m,a[N];
std::vector<int> vc;
int d[N],sd[N];
int f[N][22],g[N][22],ga[N][22],gb[N][22];
int dis(int x,int y)
{
	if(x==-1||y==-1) return 1e9;
	return abs(a[x]-a[y]);
}
int li[N],b[N];
bool cmp(int x,int y) {return a[x]<a[y];}
void dmin(int k,int l,int r)
{
	int mi=-1;
	if(l<0) l=0;
	if(r>=vc.size()) r=vc.size()-1;
	for(int i=l;i<=r;i++)
		if(mi==-1||dis(k,li[vc[i]])<dis(k,mi)) mi=li[vc[i]];
	if(mi==-1) {d[k]=sd[k]=-1;return;}
	d[k]=mi;int mi2c=-1;
	for(int i=l;i<=r;i++) if(li[vc[i]]!=mi&&(mi2c==-1||dis(k,li[vc[i]])<dis(k,mi2c))) mi2c=li[vc[i]];
	if(mi2c==-1) sd[k]=-1;
	else sd[k]=mi2c;
}
void getd()
{
	for(int i=1;i<=n;i++) li[i]=i;
	std::sort(li+1,li+n+1,cmp);
	for(int i=1;i<=n;i++) b[li[i]]=i;
	for(int i=n;i>=1;i--)
	{
		if(i<n)
		{
			int pos=lower_bound(vc.begin(),vc.end(),b[i])-vc.begin()-1;
			dmin(i,pos-1,pos+3);
		}
		vc.insert(lower_bound(vc.begin(),vc.end(),b[i]),b[i]);
	}
	d[n]=-1,sd[n]=sd[n-1]=-1;
}
int main()
{
	memset(f,0,sizeof(f));
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	getd();
	for(int i=1;i<=n;i++)
	{
		if(sd[i]!=-1) f[i][0]=d[sd[i]];
		else f[i][0]=-1;
		ga[i][0]=dis(i,sd[i]);
		gb[i][0]=dis(sd[i],d[sd[i]]);
		g[i][0]=ga[i][0]+gb[i][0];
	}
	for(int i=1;(1<<i-1)<=n;i++) for(int j=1;j<=n;j++)
	{
		if(f[j][i-1]!=-1)
		{
			f[j][i]=f[f[j][i-1]][i-1];
			g[j][i]=g[j][i-1]+g[f[j][i-1]][i-1];
			ga[j][i]=ga[j][i-1]+ga[f[j][i-1]][i-1];
			gb[j][i]=gb[j][i-1]+gb[f[j][i-1]][i-1];
		}
		else
		{
			f[j][i]=-1;
			g[j][i]=ga[j][i]=gb[j][i]=1e9;
		}
	}
	int x0;
	scanf("%d",&x0);
	int ma=1e9,mb=1,mi=-1;
	for(int i=1;i<=n;i++)
	{
		int s=i,sum=0,sa=0,sb=0;
		for(int j=20;j>=0;j--) if(f[s][j]>0&&sum+g[s][j]<=x0)
			sum+=g[s][j],sa+=ga[s][j],sb+=gb[s][j],s=f[s][j];
		if(s<n-1&&sum+dis(s,sd[s])<=x0) sa+=dis(s,sd[s]);
		if(mi==-1||1ll*ma*sb>1ll*mb*sa) ma=sa,mb=sb,mi=i;
		if((sb||mi==-1)&&1ll*ma*sb==1ll*mb*sa&&a[i]>a[mi]) mi=i;
	}
	printf("%d\n",mi);
	scanf("%d",&m);
	while(m--)
	{
		int s,x;
		scanf("%d%d",&s,&x);
		int sum=0,sa=0,sb=0;
		for(int j=20;j>=0;j--) if(f[s][j]>0&&sum+g[s][j]<=x)
			sum+=g[s][j],sa+=ga[s][j],sb+=gb[s][j],s=f[s][j];
		if(s<n-1&&sum+dis(s,sd[s])<=x) sa+=dis(s,sd[s]);
		printf("%d %d\n",sa,sb);
	}
}

\[\Huge End \]

posted @ 2025-02-01 16:43  整齐的艾萨克  阅读(57)  评论(0)    收藏  举报