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
\]
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】