【问题描述】

  C国共有$n$个城市。有$n-1$条双向道路,每条道路连接两个城市,任意两个城市之间能互相到达。小R来到C国旅行,他共规划了$m$条旅行的路线, 第$i$条旅行路线的起点是$s_i$,终点是$t_i$。在旅行过程中,小R每行走一单位长度的路需要吃一单位的食物。C国的食物只能在各个城市中买到,而且不同城市的食物价格可能不同。
  然而,小R不希望在旅行中为了购买较低价的粮食而绕远路,因此他总会选择最近的路走。现在,请你计算小R规划的每条旅行路线的最小花费是多少。

【输入格式】

  第一行包含2个整数$n$和$m$。
  第二行包含$n$个整数。第$i$个整数$w_i$表示城市$i$的食物价格。
  接下来$n-1$行,每行包括3个整数$u, v, e$,表示城市$u$和城市$v$之间有一条长为$e$的双向道路。
  接下来$m$行,每行包含2个整数$s_i$和$t_i$,分别表示一条旅行路线的起点和终点。

【输出格式】

  输出$m$行,分别代表每一条旅行方案的最小花费。

【样例输入】

6 4
1 7 3 2 5 6
1 2 4
1 3 5
2 4 1
3 5 2
3 6 1
2 5
4 6
6 4
5 6

【样例输出】

35
16
26
13

【样例说明】

对于第一条路线,小R会经过2->1->3->5。其中在城市2处以7的价格购买4单位粮食,到城市1时全部吃完,并用1
的价格购买7单位粮食,然后到达终点。

【评测用例规模与约定】

  前10%的评测用例满足:$n, m ≤ 20, w_i ≤ 20$;
  前30%的评测用例满足:$n, m ≤ 200$;
  另有40%的评测用例满足:一个城市至多与其它两个城市相连。
  所有评测用例都满足:$1 ≤ n, m ≤ 10^5,1 ≤ w_i ≤ 10^6,1 ≤ e ≤ 10000$。

【题解】

首先注意到,一条路径的选择方案,一定是从一个点走到下一个比它便宜的点,这之间的食物都在这个点购买。

而这个信息不具有可加性,却具有可减性。

之前在网上搜到了一篇自称要维护两遍单调栈三个lct的博客,但是维护单调栈的最坏时间复杂度为$O(n^2)$。

下面介绍一种基于点分治的做法。

假设当前分治中心为T,对于一个询问u->v,可以被拆成u->T,T->v,对于u->T的费用,我们可以在倍增数组上二分出u上面第一个比它便宜的位置,用这个位置的信息可以直接得出u的信息。

现在考虑T->v的费用,对于每一个询问,都附加了一个状态,表示之前便宜的费用c,我们需要在T->v的路径上找到第一个比它便宜的点设为x(这个可以通过dfs时维护一个前缀最小值数据来二分求得),这一段用的费用是dis(T, x) * c,剩下的部分就是从x向下走走到v的费用,我们可以通过dfs求出每个点到T的费用,之前已经提到过,维护的信息具有可减性,就可以$O(1)$的时间算出从一个点往下走的走到某个点的费用。

至此,问题在$O(n\log^2n)$的时间复杂度,$O(n\log n)$的空间复杂度内解决。

【代码】(滥用stl导致常数非常大)

  1. #include<bits/stdc++.h>
  2.  
  3. using namespace std;
  4.  
  5. typedef long long LL;
  6. typedef pair<int, int> pii;
  7. #define FI first
  8. #define SE second
  9. #define for_edge(u, it) for(vector<pii>::iterator it = G[u].begin(); it != G[u].end(); ++it)
  10.  
  11. const int N = + ;
  12.  
  13. vector<pii> G[N];
  14. vector<int> Q[N], q1[N], q2[N];
  15. LL dis[N], disv[N];
  16. int cost[N], sz[N], maxsz[N], top[N], root;
  17. pii q_info[N];
  18. pair<LL, int> ans[N];
  19. bool centre[N];
  20.  
  21. #define v it->FI
  22. void get_size(int u, int fa) {
  23. maxsz[u] = , sz[u] = ;
  24. for_edge(u, it) if(v != fa && !centre[v]) {
  25. get_size(v, u);
  26. sz[u] += sz[v];
  27. maxsz[u] = max(maxsz[u], sz[v]);
  28. }
  29. }
  30.  
  31. void get_root(int u, int fa, int r) {
  32. maxsz[u] = max(maxsz[u], sz[r] - sz[u]);
  33. if(maxsz[u] < maxsz[root]) root = u;
  34. for_edge(u, it) if(v != fa && !centre[v]) {
  35. get_root(v, u, r);
  36. }
  37. }
  38.  
  39. int anc[][N], val[][N];
  40.  
  41. int tot_time;
  42.  
  43. void get_top(int u, int fa, int pre) {
  44. anc[][u] = fa, val[][u] = cost[u];
  45. int tmp = clock();
  46. for(int i = ; i < ; i++) {
  47. val[i][u] = min(val[i-][u], val[i-][anc[i-][u]]);
  48. anc[i][u] = anc[i-][anc[i-][u]];
  49. }
  50. tot_time += clock() - tmp;
  51.  
  52. top[u] = pre;
  53. for_edge(u, it) if(v != fa && !centre[v]) {
  54. dis[v] = dis[u] + it->SE, get_top(v, u, pre);
  55. }
  56. }
  57.  
  58. int pre[N];
  59.  
  60. int find(int u) {
  61. int cost_u = cost[u];
  62. //int tmp = clock();
  63. for(int i = ; i >= ; i--) {
  64. if(val[i][u] >= cost_u) u = anc[i][u];
  65. }
  66. //tot_time += clock() - tmp;
  67. return u;
  68. }
  69.  
  70. void calc_1(int u, int fa) {
  71. int anc = find(u);
  72.  
  73. if(anc) pre[u] = pre[anc];
  74. else anc = root, pre[u] = u; // be root when not exist
  75. disv[u] = (dis[u] - dis[anc]) * cost[u] + disv[anc];
  76.  
  77. for(unsigned i = ; i < q1[u].size(); i++) {
  78. ans[q1[u][i]] = make_pair(disv[u], cost[pre[u]]);
  79. }
  80.  
  81. for_edge(u, it) if(v != fa && !centre[v]) {
  82. calc_1(v, u);
  83. }
  84.  
  85. }
  86.  
  87. void calc_2(int u, int fa, int fee) {
  88. static int val[N], id[N], tot;
  89. disv[u] = (dis[u] - dis[fa]) * fee + disv[fa];
  90. id[tot] = u, val[tot] = cost[u];
  91. if(tot++) val[tot-] = min(val[tot-], cost[u]);
  92.  
  93. id[tot] = u; // be u when not exist
  94. for(unsigned i = ; i < q2[u].size(); i++) {
  95. int c = q2[u][i];
  96. int anc = id[lower_bound(val, val + tot, ans[c].SE, greater<int>()) - val];
  97. ans[c].FI += ans[c].SE * (dis[anc] - dis[root]) + disv[u] - disv[anc];
  98. }
  99.  
  100. for_edge(u, it) if(v != fa && !centre[v]) {
  101. calc_2(v, u, min(fee, cost[u]));
  102. }
  103. --tot;
  104. }
  105.  
  106. void solve(int u) {
  107. if(!Q[u].size()) return;
  108.  
  109. get_size(u, );
  110. root = u, get_root(u, , u);
  111. //cerr << sz[u] << ' ' << maxsz[root] << endl;
  112.  
  113. vector<int> vec_q;
  114. vec_q.swap(Q[u]);
  115.  
  116. centre[u = root] = , top[u] = u, dis[u] = ;
  117. for(int i = ; i < ; i++) anc[i][u] = val[i][u] = ;
  118. val[][u] = cost[u];
  119. for_edge(u, it) if(!centre[v]) {
  120. dis[v] = it->SE, get_top(v, u, v);
  121. }
  122.  
  123. for(unsigned i = ; i < vec_q.size(); i++) {
  124. int c = vec_q[i], x = q_info[c].FI, y = q_info[c].SE;
  125. if(top[x] == top[y]) Q[top[x]].push_back(c);
  126. else q1[x].push_back(c), q2[y].push_back(c);
  127. }
  128.  
  129. disv[u] = , pre[u] = u;
  130. calc_1(u, );
  131.  
  132. disv[u] = ;
  133. calc_2(root, , cost[u]);
  134.  
  135. for(unsigned i = ; i < vec_q.size(); i++) {
  136. int c = vec_q[i], x = q_info[c].FI, y = q_info[c].SE;
  137. q1[x].clear(), q2[y].clear();
  138. }
  139.  
  140. for_edge(u, it) if(!centre[v]) {
  141. solve(v);
  142. }
  143. }
  144. #undef v
  145.  
  146. int main() {
  147. #ifdef DEBUG
  148. freopen("in.txt", "r", stdin);
  149. freopen("out.txt", "w", stdout);
  150. int start_time = clock();
  151. #endif
  152.  
  153. int n, m; scanf("%d%d", &n, &m);
  154. for(int i = ; i <= n; i++) {
  155. scanf("%d", cost + i);
  156. }
  157. for(int i = ; i < n; i++) {
  158. int u, v, w;
  159. scanf("%d%d%d", &u, &v, &w);
  160. G[u].push_back(pii(v, w));
  161. G[v].push_back(pii(u, w));
  162. }
  163.  
  164. for(int i = ; i < m; i++) {
  165. pii &cur = q_info[i];
  166. scanf("%d%d", &cur.FI, &cur.SE);
  167. if(cur.FI != cur.SE) Q[].push_back(i);
  168. }
  169.  
  170. solve();
  171.  
  172. for(int i = ; i < m; i++) {
  173. printf("%I64d\n", ans[i].FI);
  174. }
  175. #ifdef DEBUG
  176. fprintf(stderr, "time used : %.5fs, %.5fs\n", (double) (clock() - start_time) / CLOCKS_PER_SEC, (double)tot_time / CLOCKS_PER_SEC);
  177. #endif
  178. return ;
  179. }

第四届CCF软件能力认证(CSP2015) 第五题(最小花费)题解的更多相关文章

  1. 第四届CCF软件能力认证

    1.图像旋转 问题描述 旋转是图像处理的基本操作,在这个问题中,你需要将一个图像逆时针旋转90度. 计算机中的图像表示可以用一个矩阵来表示,为了旋转一个图像,只需要将对应的矩阵旋转即可. 输入格式 输 ...

  2. 【实(dou)力(bi)首(mai)发(meng)】第四次CCF软件能力认证题解

    这次的题总体上相对前三次偏简单.由于实力有限,就分析前四题.     试题编号:    201503-1 试题名称:    图像旋转 时间限制:    5.0s 内存限制:    256.0MB 问题 ...

  3. 第三届CCF软件能力认证

    1.门禁系统 问题描述 涛涛最近要负责图书馆的管理工作,需要记录下每天读者的到访情况.每位读者有一个编号,每条记录用读者的编号来表示.给出读者的来访记录,请问每一条记录中的读者是第几次出现. 输入格式 ...

  4. 第二届CCF软件能力认证

    1. 相邻数对 问题描述 给定n个不同的整数,问这些数中有多少对整数,它们的值正好相差1. 输入格式 输入的第一行包含一个整数n,表示给定整数的个数. 第二行包含所给定的n个整数. 输出格式 输出一个 ...

  5. 第一届CCF软件能力认证

    1.相反数 问题描述 有 N 个非零且各不相同的整数.请你编一个程序求出它们中有多少对相反数(a 和 -a 为一对相反数). 输入格式 第一行包含一个正整数 N.(1 ≤ N ≤ 500). 第二行为 ...

  6. 第六届CCF软件能力认证

    1.数位之和 问题描述 给定一个十进制整数n,输出n的各位数字之和. 输入格式 输入一个整数n. 输出格式 输出一个整数,表示答案. 样例输入 20151220 样例输出 13 样例说明 201512 ...

  7. 第五届CCF软件能力认证

    1.数列分段 问题描述 给定一个整数数列,数列中连续相同的最长整数序列算成一段,问数列中共有多少段? 输入格式 输入的第一行包含一个整数n,表示数列中整数的个数. 第二行包含n个整数a1, a2, … ...

  8. CCF计算机软件能力认证试题练习:201912-5 魔数

    CCF计算机软件能力认证试题练习:201912-5 魔数 前置知识:BFS,线段树等 \(f(x) = (x\%A)\%B\) 这个函数值的和直接用线段树维护是不太行的(也可能是我不知道),后来想了很 ...

  9. poj1985&&第四次CCF软件认证第4题 求树的直径

    Cow Marathon Time Limit: 2000MS   Memory Limit: 30000K Total Submissions: 4216   Accepted: 2137 Case ...

随机推荐

  1. C#如何控制方法的执行时间,超时则强制退出方法执行

    转自:http://outofmemory.cn/code-snippet/1762/C-how-control-method-zhixingshijian-chaoshi-ze-force-quit ...

  2. 纯JavaScript实现一些小功能

    题目链接:http://wenku.baidu.com/link?url=7Gbarr5q9X6h1QFRVAsHmfPp1xXagG209mvrJqBogseb4WLeRqbVKwxQieoh8SL ...

  3. js展开更多

    var introduces = { inIt : function(){ introduces.imgLoad(); introduces.showMore(0,'hioh',86); introd ...

  4. jquery 简单弹出层(转)

    预定义html代码:没有 所有代码通过js生成和移除. 预定义css /* 基本弹出层样式 */ .my-popup-overlay { width:100%; height:auto; /* wid ...

  5. zepto源码学习-01-整体感知

    在公司一直做移动端的项目,偶尔会做点PC端的东西,但基本上都是和移动端打交道. 移动端嘛必须上zepto,简单介绍下Zepto:它是一个面向高级浏览器的JavaScript框架的,实现JQuery的大 ...

  6. Java 单链表逆序

    代码: package com.wangzhu.linkedlist; public class LinkedListDemo { /** * @param args */ public static ...

  7. cocos2d-html5 Layer 和 Scene 创建模式

    var myLayer = cc.Layer.extend({ init:function() {//2 界面 var bRet = false; if (this._super()) { bRet ...

  8. 编译android后找不到ramdisk-u.img[已解决]

    --- --- #!/bin/bash OUTDIR=out/target/product/tiny4412AHOSTBIN=out/host/linux-x86/bin # install vend ...

  9. bzoj2753

    第一问dfs不说 第二问很容易让人想到最小树形图,但是我不会,而且时间复杂度也不允许 还有什么不同的方法呢? 首先想到的是prim的思想,设根节点已经确定,其他点未确定 我们就不断从已确定的点延伸,找 ...

  10. App性能优化

    http://www.cocoachina.com/ios/20150429/11712.html http://blog.csdn.net/jasonblog/article/details/765 ...