当前位置: 首页 > news >正文

【题解】Luogu P1081 [NOIP2012 提高组] 开车旅行

1. 海拔预处理

因为小 A 和小 B 开车涉及选择最短距离的问题,所以我们需要把他们两个开到每个城市要选择的最短距离预处理出来。

朴素做法用 \(O(n^2)\) 打擂台求出最近和第二近的城市,但显然过不了。我们可以这么做:

  • 读入时存储每个城市的高度和编号。
  • 读入完成后按照高度排序。此时与排序后的城市 \(i\) 最近与第二近的城市分别可能是 \(i-1\)\(i+1\)\(i-2\)\(i+2\)
  • 使用链表维护这些城市的高度,按原始编号把与节点以高度的顺序连接起来。
  • 在链表上按题目要求求出距离每个城市最近与第二近的城市,放在两个数组里。

实现代码如下:

void initList(){sort(p+1,p+1+n,cmp);for(int i=1;i<=n;i++){cl[p[i]].l=c[p[i-1]].cnt;cl[p[i]].r=c[p[i+1]].cnt;}for(int i=1;i<=n;i++){if(!cl[i].l&&cl[i].r){minh[i]=cl[i].r;if(cl[minh[i]].r){minh2[i]=cl[minh[i]].r;}}else if(!cl[i].r&&cl[i].l){minh[i]=cl[i].l;if(cl[minh[i]].l){minh2[i]=cl[minh[i]].l;}}else if(cl[i].l&&cl[i].r){if(c[i].h-c[cl[i].l].h<=c[cl[i].r].h-c[i].h){minh[i]=cl[i].l;if(!cl[cl[i].l].l) minh2[i]=cl[i].r;else if(c[i].h-c[cl[cl[i].l].l].h<=c[cl[i].r].h-c[i].h){minh2[i]=cl[cl[i].l].l;}else{minh2[i]=cl[i].r;}}else{minh[i]=cl[i].r;if(!cl[cl[i].r].r) minh2[i]=cl[i].l;else if(c[i].h-c[cl[i].l].h<=c[cl[cl[i].r].r].h-c[i].h){minh2[i]=cl[i].l;}else{minh2[i]=cl[cl[i].r].r;}}}cl[cl[i].l].r=cl[i].r;cl[cl[i].r].l=cl[i].l;}
}

此部分时间复杂度为 \(O(n)\)

2. 倍增预处理

很明显,每次开车暴搜是不现实的,时间复杂度会变成 \(O(mn)\)。但是因为开车只能向东开,我们可以使用倍增法来预处理每个区间旅行小 A 和小 B 开的公里数。

\(f_{i,k}\) 为从 \(i\) 城市出发,开 \(2^k\) 个循环所需要记录的信息包括小 A 和小 B 开的公里数和到达的城市。每一个循环定义为小 A 和小 B 轮流一次。

于是我们可以写出这样一段预处理代码:

void bl(){for(int i=1;i<=n;i++){if(!minh2[i]){f[i][0].ra=0;}else{f[i][0].ra=abs(c[minh2[i]].h-c[i].h);}if(!minh[minh2[i]]){f[i][0].rb=0;}else{f[i][0].rb=abs(c[minh2[i]].h-c[minh[minh2[i]]].h);}f[i][0].ep=minh[minh2[i]];}for(int k=1;k<=log(n)/log(2);k++){for(int i=1;i<=n;i++){if(f[i][k-1].ep&&f[f[i][k-1].ep][k-1].ep){f[i][k].ra=f[i][k-1].ra+f[f[i][k-1].ep][k-1].ra;f[i][k].rb=f[i][k-1].rb+f[f[i][k-1].ep][k-1].rb;f[i][k].ep=f[f[i][k-1].ep][k-1].ep; }}}
}

这一部分的时间复杂度是典型的倍增 \(O(n\log n)\)

3. 求解

预处理完成之后就可以求解了。这一部分与 LCA 类似,从大到小遍历 \(k\),如果 \(f_{s,k}\) 的路程不超过限定的 \(x\) 那么就往前走,否则继续减小 \(k\)。如果可行开车的天数不是 \(2\) 的倍数,也就是两个人能完成一个完整的循环时,最后就要再加上小 A 能开的最后一段路。加一个特判即可。

void calc(int s,int x){ta=tb=0;for(int k=log(n)/log(2);k>=0;k--){if(f[s][k].ep&&f[s][k].ra+f[s][k].rb<=x){ta+=f[s][k].ra;tb+=f[s][k].rb;x-=f[s][k].ra+f[s][k].rb;s=f[s][k].ep;}}if(minh2[s]&&abs(c[s].h-c[minh2[s]].h)<=x){ta+=abs(c[s].h-c[minh2[s]].h);}
}

此部分时间复杂度与 LCA 类似,\(O(\log n)\)

Part 4: 完成 Task 1&Task 2

有了上面的代码这两个问题就都能够迎刃而解了。

Task 1

这个问题遍历每个节点并求解,用一个 double 类型变量记录比值的最小值,找到这个最小值。如果最小值相同那么找海拔更高的。如果小 B 的路程为 \(0\) 就单独特判,记录海拔更高的。

void tsk1(){minb=9223372036854775807;for(int i=1;i<=n;i++){calc(i,x0);if(tb!=0){if(1.0*ta/tb<minb){minb=1.0*ta/tb;ans1=i;}else if(1.0*ta/tb==minb){if(c[i].h>c[ans1].h) ans1=i;}}else{if(minb==9223372036854775807&&c[i].h>c[ans1].h) ans1=i;}}printf("%lld\n",ans1);
}

时间复杂度 \(O(n\log n)\)

Task 2

这个问题就更简单了,依次求解 \(m\) 个问题即可。

void tsk2(){for(int i=1;i<=m;i++){calc(s[i],x[i]);printf("%lld %lld\n",ta,tb);}
}

总结

此题思维不是特别难,但是细节较多,码量很大。

总时间复杂度应该是 \(O((n+m)\log n)\)

完整代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define int long long
using namespace std;
const int maxn=1e5+10;
struct city{int h,cnt;
}c[maxn];
struct List{int l,r;
}cl[maxn];
struct Node{int ra,rb,ep;
}f[maxn][40]; 
int n,m,x0,ta,tb,ans1;
double minb;
int s[maxn],x[maxn],p[maxn];
int minh[maxn],minh2[maxn];
bool cmp(int a,int b){return c[a].h<c[b].h;
}
void Read(){scanf("%lld",&n);for(int i=1;i<=n;i++){scanf("%lld",&c[i].h);c[i].cnt=p[i]=i;}scanf("%lld%lld",&x0,&m);for(int i=1;i<=m;i++){scanf("%lld%lld",&s[i],&x[i]);}
}
void initList(){sort(p+1,p+1+n,cmp);for(int i=1;i<=n;i++){cl[p[i]].l=c[p[i-1]].cnt;cl[p[i]].r=c[p[i+1]].cnt;}for(int i=1;i<=n;i++){if(!cl[i].l&&cl[i].r){minh[i]=cl[i].r;if(cl[minh[i]].r){minh2[i]=cl[minh[i]].r;}}else if(!cl[i].r&&cl[i].l){minh[i]=cl[i].l;if(cl[minh[i]].l){minh2[i]=cl[minh[i]].l;}}else if(cl[i].l&&cl[i].r){if(c[i].h-c[cl[i].l].h<=c[cl[i].r].h-c[i].h){minh[i]=cl[i].l;if(!cl[cl[i].l].l) minh2[i]=cl[i].r;else if(c[i].h-c[cl[cl[i].l].l].h<=c[cl[i].r].h-c[i].h){minh2[i]=cl[cl[i].l].l;}else{minh2[i]=cl[i].r;}}else{minh[i]=cl[i].r;if(!cl[cl[i].r].r) minh2[i]=cl[i].l;else if(c[i].h-c[cl[i].l].h<=c[cl[cl[i].r].r].h-c[i].h){minh2[i]=cl[i].l;}else{minh2[i]=cl[cl[i].r].r;}}}cl[cl[i].l].r=cl[i].r;cl[cl[i].r].l=cl[i].l;}
}
void bl(){for(int i=1;i<=n;i++){if(!minh2[i]){f[i][0].ra=0;}else{f[i][0].ra=abs(c[minh2[i]].h-c[i].h);}if(!minh[minh2[i]]){f[i][0].rb=0;}else{f[i][0].rb=abs(c[minh2[i]].h-c[minh[minh2[i]]].h);}f[i][0].ep=minh[minh2[i]];}for(int k=1;k<=log(n)/log(2);k++){for(int i=1;i<=n;i++){if(f[i][k-1].ep&&f[f[i][k-1].ep][k-1].ep){f[i][k].ra=f[i][k-1].ra+f[f[i][k-1].ep][k-1].ra;f[i][k].rb=f[i][k-1].rb+f[f[i][k-1].ep][k-1].rb;f[i][k].ep=f[f[i][k-1].ep][k-1].ep; }}}
}
void calc(int s,int x){ta=tb=0;for(int k=log(n)/log(2);k>=0;k--){if(f[s][k].ep&&f[s][k].ra+f[s][k].rb<=x){ta+=f[s][k].ra;tb+=f[s][k].rb;x-=f[s][k].ra+f[s][k].rb;s=f[s][k].ep;}}if(minh2[s]&&abs(c[s].h-c[minh2[s]].h)<=x){ta+=abs(c[s].h-c[minh2[s]].h);}
}
void tsk1(){minb=9223372036854775807;for(int i=1;i<=n;i++){calc(i,x0);if(tb!=0){if(1.0*ta/tb<minb){minb=1.0*ta/tb;ans1=i;}else if(1.0*ta/tb==minb){if(c[i].h>c[ans1].h) ans1=i;}}else{if(minb==9223372036854775807&&c[i].h>c[ans1].h) ans1=i;}}printf("%lld\n",ans1);
}
void tsk2(){for(int i=1;i<=m;i++){calc(s[i],x[i]);printf("%lld %lld\n",ta,tb);}
}
signed main(){Read();initList();bl();tsk1();tsk2();return 0;
}
http://www.jsqmd.com/news/79275/

相关文章:

  • 2025年AI图文创作神器01Agent:3步解决‘死图‘痛点,效率提升300%
  • 2025年AI图文创作神器01Agent:3步解决‘死图‘痛点,效率提升300%
  • 如何编写优美的代码:从工匠到艺术家的修炼之路
  • 做字幕不再靠 Pr?一次带你体验真正的省时做法
  • AI搜索焦虑自救指南:一份面向2026年的系统化追赶方案
  • 常见报错org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): org.example.dem
  • 【题解】Codeforces 1986B Matrix Stabilization
  • 【题解】Luogu P6092 [CEOI2012] 工作规划
  • 告别文件整理拖延症!快速找关键字 TXT + 批量复制到目标文件夹,躺平搞定
  • [Non]树上乘法
  • 【笔记】强连通分量
  • 告别信息传递繁琐步骤!批量查询+手机条形码一键发,1次搞定全传递
  • 视频剪辑软件电脑版排行榜,2025年度前十名软件推荐
  • 《追问者宪章》完整版
  • 重练算法(代码随想录版) day38 - 动态规划part6
  • 笑不活!男人假装爱你,7 个 “演技信号” 速查!
  • 1-Year XTOOL D9 Update Service: Latest Diagnostics for European/American Vehicles
  • 【笔记】最近公共祖先 Tarjan
  • Error occurred during initialization of VMCould not reserve enough space for object heap
  • 【算法题】滑动窗口(一)
  • 东芝与Quantum Corridor实现量子安全网络通信重大突破
  • 基于java的SpringBoot/SSM+Vue+uniapp的零工市场服务系统的详细设计和实现(源码+lw+部署文档+讲解等)
  • 为什么越来越多的IT技术人员转行网络安全?零基础入门到精通,收藏这一篇就够了
  • 甲骨文AI投资支出激增致股价创24年最大跌幅
  • 基于java的SpringBoot/SSM+Vue+uniapp的旅游出行指南系统的详细设计和实现(源码+lw+部署文档+讲解等)
  • HTTP协议在C#大文件上传中如何处理重试逻辑?
  • 转行IT最吃香的六大岗位:从零到精通,就业无忧!
  • 【笔记】基本数论
  • 19、将 Snort 规则转换为 iptables 规则
  • 计算计算机专业内卷严重,普通毕业生何去何从?​这个风口行业缺口炸了,现在入行正当时!