题目大意

  小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市i 的海拔高度为Hi,城市i 和城市j 之间的距离d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i,j] = |Hi – Hj|。
  旅行过程中,小A和小B轮流开车,第一天小A开车,之后每天轮换一次。他们计划选择一个城市S作为起点,一直向东行驶,并且最多行驶X公里就结束旅行。小A和小B的驾驶风格不同,小B总是沿着前进方向选择一个最近的城市作为目的地,而小A总是沿着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出X公里,他们就会结束旅行。
  在启程之前,小A想知道两个问题:
  1.对于一个给定的X=X0,从哪一个城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值最小(如果小B的行驶路程为0,此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
  2. 对任意给定的X=Xi 和出发城市Si,小A开车行驶的路程总数以及小B行驶的路程总数。

题解

如何处理对小A小B一个城市的下一个城市

  法一:二叉平衡树,key值城市高度。从右往左扫描,见一个城市就往里塞,然后从节点的前驱、后继以及前驱的前驱、后继的后继中选取最小值和次小值。这个可以用set的iterator来实现。

  法二:双向链表。将所有城市排序接成链表,从左往右扫描,在cur->Prev->Prev, cur->Prev, cur->Next, cur->Next->Next中选取最小值和次小值,然后将其删除。

如何求解

  树上倍增。将所有城市复制一份,一份表示A开车,一份表示B开车,根据A,B城市之间的转移在两个集合之间连边,出度为0的节点向根节点连边。边权有3个:A行驶距离,B行驶距离(在连接A, B集合的边中,此两个量必然有一个为0),总行驶距离。对这三个权值进行倍增统计,随后各个事情就简单了。

注意事项

  对于这种题意复杂的题,最好把限制条件写在纸上,不然记着记着就记错了,浪费大把时间。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <set>
  5. #include <cmath>
  6. #include <vector>
  7. using namespace std;
  8.  
  9. const int MAX_CITY = 100010, SuperINF = 2147483647;
  10.  
  11. struct City
  12. {
  13. int Height, Id;
  14.  
  15. bool operator < (const City& a) const
  16. {
  17. return Height < a.Height;
  18. }
  19. }_cities[MAX_CITY];
  20. int TotCity;
  21.  
  22. struct Graph
  23. {
  24. private:
  25. static const int MAX_NODE = MAX_CITY * 2 + 50;
  26.  
  27. struct Node
  28. {
  29. unsigned int Sum[3][20];
  30. Node *Elder[20];
  31. vector<Node*> Next;
  32. int Depth;
  33. }_nodes[MAX_NODE], *Root;
  34. int TotNode;
  35.  
  36. void Dfs(Node *cur, Node *fa, int depth)
  37. {
  38. cur->Depth = depth;
  39. if (cur != Root)
  40. {
  41. for (int i = 1; cur->Elder[i - 1]->Elder[i - 1]; i++)
  42. {
  43. cur->Elder[i] = cur->Elder[i - 1]->Elder[i - 1];
  44. for (int j = 0; j < 3; j++)
  45. cur->Sum[j][i] = cur->Sum[j][i - 1] + cur->Elder[i - 1]->Sum[j][i - 1];
  46. }
  47. }
  48. for (int i = 0; i < cur->Next.size(); i++)
  49. if (cur->Next[i] != fa)
  50. Dfs(cur->Next[i], cur, depth + 1);
  51. }
  52.  
  53. int Log2(int x)
  54. {
  55. int ans = 0;
  56. while (x >>= 1)
  57. ans++;
  58. return ans;
  59. }
  60.  
  61. void Query(Node *cur, int dist, int *ans)
  62. {
  63. for (int i = 0; i < 3; i++)
  64. ans[i] = 0;
  65. int topFa = Log2(cur->Depth);
  66. for (int i = topFa; i >= 0; i--)
  67. {
  68. if (cur->Elder[i] && cur->Sum[2][i] <= dist)
  69. {
  70. for (int j = 0; j < 3; j++)
  71. ans[j] += cur->Sum[j][i];
  72. dist -= cur->Sum[2][i];
  73. cur = cur->Elder[i];
  74. }
  75. }
  76. }
  77.  
  78. public:
  79. void Init(int totNode, int root)
  80. {
  81. TotNode = totNode;
  82. Root = _nodes + root;
  83. }
  84.  
  85. void Build(int u, int v, int w, bool isA)
  86. {
  87. Node *cur = _nodes + u, *fa = _nodes + v;
  88. cur->Elder[0] = fa;
  89. fa->Next.push_back(cur);
  90. if (isA)
  91. cur->Sum[0][0] = cur->Sum[2][0] = w;
  92. else
  93. cur->Sum[1][0] = cur->Sum[2][0] = w;
  94. }
  95.  
  96. void GetSum()
  97. {
  98. Dfs(Root, NULL, 0);
  99. }
  100.  
  101. void Query(int start, int dist, int *ans)
  102. {
  103. return Query(_nodes + start, dist, ans);
  104. }
  105. }g;
  106.  
  107. int GetLDelta(set<City>& tree, set<City>::iterator cur)
  108. {
  109. if (cur == tree.begin())
  110. return SuperINF;
  111. set<City>::iterator l = cur;
  112. l--;
  113. return abs(cur->Height - l->Height);
  114. }
  115.  
  116. int GetRDelta(set<City>& tree, set<City>::iterator cur)
  117. {
  118. set<City>::iterator r = cur;
  119. r++;
  120. if (r == tree.end())
  121. return SuperINF;
  122. return abs(cur->Height - r->Height);
  123. }
  124.  
  125. void BuildGraph()
  126. {
  127. g.Init(TotCity * 2 + 1, TotCity * 2 + 1);
  128. static set<City> tree;
  129. for (int i = TotCity; i >= 1; i--)
  130. {
  131. tree.insert(_cities[i]);
  132. set<City>::iterator cur = tree.find(_cities[i]);
  133. unsigned int lDelta = GetLDelta(tree, cur);
  134. unsigned int rDelta = GetRDelta(tree, cur);
  135. if (lDelta < SuperINF || rDelta < SuperINF)
  136. {
  137. if (lDelta <= rDelta)
  138. {
  139. set<City>::iterator l = cur;
  140. l--;
  141. g.Build(cur->Id + TotCity, l->Id, lDelta, false);
  142. unsigned int llDelta = GetLDelta(tree, l) + lDelta;
  143. if (llDelta < SuperINF || rDelta < SuperINF)
  144. {
  145. if (llDelta <= rDelta)
  146. {
  147. l--;
  148. g.Build(cur->Id, l->Id + TotCity, llDelta, true);
  149. }
  150. else
  151. {
  152. set<City>::iterator r = cur;
  153. r++;
  154. g.Build(cur->Id, r->Id + TotCity, rDelta, true);
  155. }
  156. }
  157. else
  158. g.Build(cur->Id, TotCity * 2 + 1, SuperINF, true);
  159. }
  160. else
  161. {
  162. set<City>::iterator r = cur;
  163. r++;
  164. g.Build(cur->Id + TotCity, r->Id, rDelta, false);
  165. unsigned int rrDelta = GetRDelta(tree, r) + rDelta;
  166. if (lDelta < SuperINF || rrDelta < SuperINF)
  167. {
  168. if (lDelta <= rrDelta)
  169. {
  170. set<City>::iterator l = cur;
  171. l--;
  172. g.Build(cur->Id, l->Id + TotCity, lDelta, true);
  173. }
  174. else
  175. {
  176. r++;
  177. g.Build(cur->Id, r->Id + TotCity, rrDelta, true);
  178. }
  179. }
  180. else
  181. g.Build(cur->Id, TotCity * 2 + 1, SuperINF, true);
  182. }
  183. }
  184. else
  185. {
  186. g.Build(cur->Id + TotCity, TotCity * 2 + 1, SuperINF, true);
  187. g.Build(cur->Id, TotCity * 2 + 1, SuperINF, false);
  188. }
  189. }
  190. g.GetSum();
  191. }
  192.  
  193. void Read()
  194. {
  195. scanf("%d", &TotCity);
  196. for (int i = 1; i <= TotCity; i++)
  197. {
  198. scanf("%d", &_cities[i].Height);
  199. _cities[i].Id = i;
  200. }
  201. }
  202.  
  203. void Sol1()
  204. {
  205. int dist0, ansStart = 0;
  206. double ansRatio = SuperINF;
  207. scanf("%d", &dist0);
  208. int ans[3];
  209. for (int i = 1; i <= TotCity; i++)
  210. {
  211. g.Query(i, dist0, ans);
  212. double curRatio = (ans[1] == 0 ? SuperINF : 1.0 * ans[0] / ans[1]);
  213. if (curRatio < ansRatio || (abs(curRatio - ansRatio) < 0.0000001 && _cities[i].Height > _cities[ansStart].Height))
  214. {
  215. ansRatio = curRatio;
  216. ansStart = i;
  217. }
  218. }
  219. printf("%d\n", ansStart);
  220. }
  221.  
  222. void Sol2()
  223. {
  224. int qCnt;
  225. int ans[3];
  226. scanf("%d", &qCnt);
  227. while (qCnt--)
  228. {
  229. int start, dist;
  230. scanf("%d%d", &start, &dist);
  231. g.Query(start, dist, ans);
  232. printf("%d %d\n", ans[0], ans[1]);
  233. }
  234. }
  235.  
  236. int main()
  237. {
  238. Read();
  239. BuildGraph();
  240. Sol1();
  241. Sol2();
  242. return 0;
  243. }

  

luogu1081 开车旅行 树上倍增的更多相关文章

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

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

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

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

  3. 【NOIP2012】开车旅行(倍增)

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

  4. NOIP2012开车旅行 【倍增】

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

  5. 2018.11.04 洛谷P1081 开车旅行(倍增)

    传送门 思路简单码量超凡? 感觉看完题大家应该都知道是倍增sbsbsb题了吧. 首先预处理出从每个点出发如果是AAA走到哪个点,如果是BBB走到哪个点. 然后利用刚刚预处理出的信息再预处理从每个点出发 ...

  6. [luogu1081] 开车旅行

    题面 ​ 这个题目还是值得思考的. ​ 看到这个题目, 大家应该都想到了这样一个思路, 就是把每个点能够达到的最近的和次近的点都预处理出来, 然后跑就可以了, 现在问题就是难在这个预处理上面, 我们应 ...

  7. NOIP2012 T3开车旅行 set+倍增

    70分做法: 先预处理出所有点的最近和次近(O(n^2)一遍就OK) 然后暴力求出每个解(O(nm)) //By SiriusRen #include <cstdio> #include ...

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

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

  9. 开车旅行 2012年NOIP全国联赛提高组(倍增+set)

    开车旅行 2012年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond     题目描述 Description 小A 和小B决定利用 ...

随机推荐

  1. Resources.getResourceAsReader 报空指针

    1.maven项目中 可能未编译 导致找不到, 解决方法:mvn clean install -DskipTests -X   编译一下项目 2.可能在 Resources.getResourceAs ...

  2. Prime算法生成最小生成树

    虽说是生成树,但我只将生成的边输出了.至于怎么用这些边来创建树...我不知道_(:з」∠)_ //Prime方法生成最小生成树 void GraphAdjacencyListWeight::Gener ...

  3. (三)Python 学习第三天--GUI桌面项目

    (代码参考了别人的代码,只做学习用途!!!最近因为写论文,好久没有记录,好内疚...今天学习了一个小案例,做一下) 主要使用模块:tkinter 代码如下: from tkinter import * ...

  4. windows环境搭建ui自动化环境

    windows环境安装python虚拟环境 https://www.cnblogs.com/suke99/p/5355894.html https://www.cnblogs.com/jiuyang/ ...

  5. 原生js实现瀑布流效果

    参考此篇:https://segmentfault.com/a/1190000012621936 以下为个人测试中: css: .masonry{ width:100%; } .item{ posit ...

  6. 【Android】进程间通信IPC——Binder

    Binder是Android中的跨进程通信方式,bindService的时候,服务端返回Binder对象,通过该对象客户端可以从服务端获取数据.在进程间通信IPC——AIDL中创建了ICustomAi ...

  7. BZOJ 4561: [JLoi2016]圆的异或并 扫描线 + set

    看题解看了半天...... Code: #include<bits/stdc++.h> #define maxn 200010 #define ll long long using nam ...

  8. ubuntu_linux /boot/grub/grub.conf

    ==========================================UBUNTU  /boot/grub/grub.conf文件============================ ...

  9. hdu2014 青年歌手大奖赛_评委会打分【C++】

    青年歌手大奖赛_评委会打分 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  10. 获取当前日期,或指定日期的农历js代码

    时间不早了,直接上代码啦-- var CalendarData=new Array(100);var madd=new Array(12);var tgString="甲乙丙丁戊己庚辛壬癸& ...