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 @   整齐的艾萨克  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示