题面

​ 这个题目还是值得思考的.

​ 看到这个题目, 大家应该都想到了这样一个思路, 就是把每个点能够达到的最近的和次近的点都预处理出来, 然后跑就可以了, 现在问题就是难在这个预处理上面, 我们应该如何做呢? 观察到, 近的概念是两点之间的海拔距离最小, 所以我们可以将海拔距离从后往前(毕竟你只能往你后面的地方走嘛...), 扔进一个可以维护大小关系的数据结构中, 不妨设当前点海拔进入这个数据结构后位置为\(k\), 那么我们只需要比较位置为\(k - 1\), \(k - 2\), \(k + 1\), \(k + 2\)的四个数就可以了, 大家可以自己在纸上分类讨论一下, 这里就不做过多的阐述了. 现在我们只需要知道这个数据结构就可以了, 大家想一想, 有什么数据结构可以快速的找到某个数排序后的位置呢? 平衡树, 查找和插入的复杂度都为\(\log_ {2}{n}\)了, 这样我们就可以快速寻找了, 当然, 因为有重复结构, 所以用STL中的\(multiset\)是比较好的一种办法(其实是我不会双向链表), 这样我们就将比较难想的预处理部分弄出来了.

​ 当然, 想完了预处理后我们就可以想接下来怎么走了. 比较快速的方法是倍增, \(f[i][j][opt]\)表示当前位置为\(i\), 已经走了\(2 ^j\)天, \(opt\)为1时代表从\(i\)位置开始走, 第一个走的是B所走到的位置, 那么\(opt\)是\(0\)时就是A了. \(disa[i][j][opt]\)代表在第\(i\)位出发, 走了\(2 ^ j\)天, \(opt\)为1代表从\(i\)开始走, B先出发, 在这段时间中A走的距离, opt为\(0\)自己推一下吧, 实在是不想打了, \(disb[i][j][opt]\)也自己照着看一下吧. 我们在预处理出每个点的最近点和次近点后, 就可以处理出来了.

​ 至此, 预处理完毕, 剩下的代码中会说到的.

具体代码

  1. #include <iostream>
  2. #include <cstring>
  3. #include <cstdio>
  4. #include <set>
  5. #define N 100005
  6. using namespace std;
  7. int n, x0, m, f[N][22][2], disa[N][22][2], disb[N][22][2], bin;
  8. struct node
  9. {
  10. int id, h;
  11. bool operator < (const node &p) const { return h < p.h; }
  12. } ht[N];
  13. multiset<node> s;
  14. multiset<node> :: iterator it;
  15. inline int read()
  16. {
  17. int x = 0, w = 1;
  18. char c = getchar();
  19. while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
  20. while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
  21. return x * w;
  22. }
  23. inline void get_ans(int S, int &dist_a, int &dist_b, int limit)
  24. {
  25. int now = S;
  26. for(int i = 20; i >= 0; i--)
  27. if(f[now][i][0] && disa[now][i][0] + disb[now][i][0] + dist_a + dist_b <= limit)//倍增跑, 注意当没有能够跑的地方或者超过了给定的值的话就需要continue
  28. {
  29. dist_a += disa[now][i][0];
  30. dist_b += disb[now][i][0];
  31. now = f[now][i][0];
  32. }
  33. }
  34. inline long long abs(long long x) { return x < 0 ? -x : x; }
  35. inline void init()
  36. {
  37. for(int i = n; i >= 1; i--)
  38. {
  39. int Ato, Bto; node nxt, pre;
  40. s.insert(ht[i]);//先插入当前海拔, 方便找
  41. it = s.lower_bound(ht[i]);//找到当前海拔在序列中的位置
  42. it++; nxt = (*it);
  43. it--; it--; pre = (*it);
  44. it++;
  45. if(abs(nxt.h - ht[i].h) < abs(pre.h - ht[i].h))
  46. {
  47. it++, it++;
  48. Bto = nxt.id;
  49. if(abs(pre.h - ht[i].h) > abs((*it).h - ht[i].h)) Ato = (*it).id;
  50. else Ato = pre.id;
  51. }
  52. else
  53. {//同上
  54. it--, it--;
  55. Bto = pre.id;
  56. if(abs(nxt.h - ht[i].h) >= abs((*it).h - ht[i].h)) Ato = (*it).id;
  57. else Ato = nxt.id;
  58. }
  59. //注意, 这里由于前后海拔差相同的话取海拔较低的, 所以有的地方有等于号, 有的地方没有, 希望大家能够自己好好去想一下为什么, 有问题可以私信我(反正我不会回答的(手动删除)).
  60. f[i][0][0] = Ato; f[i][0][1] = Bto;
  61. disa[i][0][0] = abs(ht[Ato].h - ht[i].h); disb[i][0][0] = 0;
  62. disb[i][0][1] = abs(ht[Bto].h - ht[i].h); disa[i][0][1] = 0;
  63. }
  64. for(int i = 1; i <= n; i++)//由于上面并没有讨论到A, B都走过了的情况, 只讨论了A和B单独走的情况, 故在这里要处理一下.
  65. {
  66. f[i][1][0] = f[f[i][0][0]][0][1];
  67. f[i][1][1] = f[f[i][0][1]][0][0];
  68. disa[i][1][1] = abs(ht[f[i][1][1]].h - ht[f[i][0][1]].h); disb[i][1][0] = abs(ht[f[i][1][0]].h - ht[f[i][0][0]].h);
  69. disa[i][1][0] = disa[i][0][0]; disb[i][1][1] = disb[i][0][1]; //类似于倍增LCA中的预处理
  70. }
  71. for(int j = 2; j <= 20; j++)
  72. for(int i = 1; i <= n; i++)
  73. {
  74. f[i][j][0] = f[f[i][j - 1][0]][j - 1][0];
  75. f[i][j][1] = f[f[i][j - 1][1]][j - 1][1];
  76. disa[i][j][0] = disa[i][j - 1][0] + disa[f[i][j - 1][0]][j - 1][0];
  77. disb[i][j][0] = disb[i][j - 1][0] + disb[f[i][j - 1][0]][j - 1][0];
  78. disa[i][j][1] = disa[i][j - 1][1] + disa[f[i][j - 1][1]][j - 1][0];
  79. disb[i][j][1] = disb[i][j - 1][1] + disb[f[i][j - 1][1]][j - 1][0];
  80. //仔细思考一下, 应该不难
  81. }
  82. }
  83. int main()
  84. {
  85. n = read();
  86. for(int i = 1; i <= n; i++) { ht[i].h = read(); ht[i].id = i; }
  87. ht[0].h = 2e9 + 7; ht[n + 1].h = -(2e9 + 7); ht[0].id = 0; ht[n + 1].id = n + 1;
  88. //我取2147483647爆掉了, 调了整整一上午, 所以取极值的时候需要注意一下
  89. s.insert(ht[0]); s.insert(ht[0]); s.insert(ht[n + 1]); s.insert(ht[n + 1]);
  90. //插入两个极小值和极大值代表我们在找到位置k的时候, 他前后总会至少有两个点, 免去了分类讨论的冗杂
  91. init(); //预处理
  92. x0 = read(); m = read();
  93. int id = 0;
  94. double ans = 1e16;
  95. for(int i = 1; i <= n; i++)//枚举每一个点为起点时的比例
  96. {
  97. int dist_a = 0, dist_b = 0;
  98. get_ans(i, dist_a, dist_b, x0);
  99. if(dist_b == 0)
  100. {
  101. if(ans > 1e14) { ans = 1e14; id = i; }
  102. else if(ans == 1e14 && ht[i].h > ht[id].h) id = i;
  103. }
  104. else
  105. {
  106. double res = (double) dist_a / dist_b;
  107. if(res < ans) { ans = res; id = i; }
  108. else if(res == ans && ht[i].h > ht[id].h) id = i;
  109. }
  110. }
  111. printf("%d\n", id);
  112. for(int i = 1; i <= m; i++)
  113. {
  114. bin = read(); x0 = read();
  115. int dist_a = 0, dist_b = 0;
  116. get_ans(bin, dist_a, dist_b, x0);
  117. printf("%d %d\n", dist_a, dist_b);
  118. }
  119. return 0;
  120. }

[luogu1081] 开车旅行的更多相关文章

  1. luogu1081 开车旅行 树上倍增

    题目大意 小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市i 的海拔高度为Hi,城市i 和城市j 之间的距离 ...

  2. luogu1081 开车旅行2012 D1T3 (倍增,set,O2)

    题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi,城市 i ...

  3. $Noip2012\ Luogu1081$ 开车旅行 倍增优化$ DP$

    Luogu Description Sol 1.发现对于每个城市,小A和小B的选择是固定的,可以预处理出来,分别记为ga[],gb[] 2.并且,只要知道了出发城市和出发天数,那么当前城市和小A,小B ...

  4. Luogu 1081 【NOIP2012】开车旅行 (链表,倍增)

    Luogu 1081 [NOIP2012]开车旅行 (链表,倍增) Description 小A 和小B决定利用假期外出旅行,他们将想去的城市从1到N 编号,且编号较小的城市在编号较大的城市的西边,已 ...

  5. CH5701 开车旅行

    题意 5701 开车旅行 0x50「动态规划」例题 描述 小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 ...

  6. 2012Noip提高组Day1 T3 开车旅行

    题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的 城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 Hi,城市 ...

  7. Cogs 1264. [NOIP2012] 开车旅行(70分 暴力)

    1264. [NOIP2012] 开车旅行 ★★☆   输入文件:drive.in   输出文件:drive.out   简单对比时间限制:2 s   内存限制:128 MB [题目描述] 小A 和小 ...

  8. 开车旅行 【NOIP2012 D1T3】

    开车旅行 [NOIP2012 D1T3] 倍增 首先令\(a[i]\)表示从i出发最近的城市下标,\(b[i]\)表示从i出发第二近的城市下标 可以维护一个\(\text{set<pair< ...

  9. 洛谷 P1081 开车旅行(70)

    P1081 开车旅行 题目描述 小AA 和小BB 决定利用假期外出旅行,他们将想去的城市从 11到 NN 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 ii的海 ...

随机推荐

  1. 使用rem编写自适应屏幕网页造成div被span撑高的解决办法

    原始代码: <html> <head> <meta charset="utf-8"> <meta content="ie=edg ...

  2. Java虚拟机 - 符号引用和直接引用理解

    java -- JVM的符号引用和直接引用 https://www.zhihu.com/question/50258991 在JVM中类加载过程中,在解析阶段,Java虚拟机会把类的二级制数据中的符号 ...

  3. 理解RESTFul和SOA

    RESTFul:面向资源的架构(roa) RESTFul基于HTTP协议,是一种明确构建在客户端/服务端体系结构上的一种风格, rest是Representational State Transfer ...

  4. [翻译]Review——24 tips for React Native you probably want to know

    Post author: Albert Gao Post link: http://www.albertgao.xyz/2018/05/30/24-tips-for-react-native-you- ...

  5. js实现链式操作

    前言:前不久阿里远程面试时问了我一个问题,如下: function Person(){}; var person = new Person(); //实现person.set(10).get()返回2 ...

  6. 前端学习之HTML(1)

    HTML标签学习 2018-10-31 记录一下学习的网站 http://www.w3school.com.cn http://www.runoob.com/ <!DOCTYPE html> ...

  7. spring作用、spring注解、管理对象的作用域与生命周期、自动装配、Spring的框架包有哪些作用是什么

    Spring 1. 作用 创建和管理对象,使得开发过程中,可以不必使用new关键字创建对象,而是直接获取对象!并且,还可以通过一些配置,使得某些获取到的对象,其中某些属性已经是被赋值的! 2. Spr ...

  8. mysql_real_escape_string与mysqli_real_escape_string

    参考 mysql_real_escape_string  mysqli_real_escape_string mysql_real_escape_string是用来转义字符的,主要是转义POST或GE ...

  9. react组件直接在document上添加事件

    demo:比如组件里有个div写的框框,点击document body的背景色变红,点击div写的框框没效果 componentDidMount(){ document.onclick = this. ...

  10. Android平台接入Facebook登录

    官方教程地址: https://developers.facebook.com/docs/android/getting-started 开发环境为Android Studio,官方要求SDK最低版本 ...