树链剖分+线段树

思路

貌似题解里没有树链剖分和线段树的,贡献一发。

首先明确题目要求:一辆车走某条路从x城到y城的边权最小值

我们把要求分开来看:

  1. 从x城到y城:我们需要走的路径将两点联通

  2. 边权最小值:我们要找这条路上的限重最小值

如果你是一个货车司机(而且题目还告诉你你的汽车走多远不要油),你肯定想多运一些货物,也就要求联通两点的权值尽可能大。

又要保证联通,又要保证权值尽可能大,没错,我们需要用到最小生成树。

(如果还不理解,你可以设想一下,有两条都可以从a到b,一条路限重10,一条路限重100,你一定会选择第二条路;我们再推广一下,如果两条路都能联通还未联通的a、b两个联通块(你可以认为a、b是两个岛,两条路是跨岛大桥),一条路限重10,一条路限重100,你还是一定会选择第二条路)

最小生成树的方法:先按边权大小排序,利用并查集判断两块是否联通,生成一个新的图


好,现在第一个问题解决了:你运货的最大路径方案一定在新的图(树)上了,怎么求两点之间权值最小的呢?

因为这是一棵树,所以两点之间路径唯一,可是直接搜索时间又肯定承受不住,我们这时就可以采用树链剖分了

这是类似树剖板题的题,就有提到求某两点的最值问题

值得一提的是:树剖+线段树只是支持修改和查询点权的,这时我们就需要知道怎么将边权转换为点权

边权与点权之间的转换

随便在网上找了个图:我们这样实现边权与点权之间的转换:将根节点的点权设为INF,然后所有边权下放到连接的点(所有边权往下挪到了点里,由于根节点值为INF不影响min的计算(同理,查询最大值就设为-INF))

然后直接查询就好啦!

怎么可能?!

刚开始的时候,我转换完后就直接像树剖板题那样求最值了,结果只有10分,那么问题出在哪呢?

我们看一下这个图(黑色是边权,黄色是转换后的点权):

若想查询A点到B点的最值,我们会发现,按普通树剖的查询方法,我们会访问20那个点(5-20-19-8),然而应该访问的路径是5-19-8,所以我们要对查询函数做一些修改,“绕开那些点”

  1. void getans(int x,int y){
  2. if(findfather(x) != findfather(y)){
  3. printf("-1\n");
  4. return ;
  5. }
  6. int ans = INF;
  7. while(top[x] != top[y]){
  8. if(dep[top[x]] < dep[top[y]])swap(x,y);
  9. ans = min(ans,query(1,pos[top[x]],pos[x]));
  10. x = fa[top[x]];
  11. }
  12. if(x == y){
  13. printf("%d\n",ans);//绕开
  14. return ;
  15. }
  16. if(dep[x] > dep[y])swap(x,y);
  17. ans = min(ans,query(1,pos[x] + 1,pos[y]));//+1绕开
  18. printf("%d\n",ans);
  19. }

AC代码

  1. #include<iostream>
  2. #include<vector>
  3. #include<queue>
  4. #include<cstdio>
  5. #include<cstring>
  6. #include<algorithm>
  7. using namespace std;
  8. int RD(){
  9. int out = 0,flag = 1;char c = getchar();
  10. while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
  11. while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
  12. return flag * out;
  13. }
  14. const int maxn = 500190,INF = 999999999;
  15. int num,nr,nume,na,cnt,numt;
  16. int head[maxn];
  17. struct Node{
  18. int v,nxt,dis;
  19. }E[maxn * 2];
  20. void add(int u,int v,int dis){
  21. E[++nume].nxt = head[u];
  22. E[nume].v = v;
  23. E[nume].dis = dis;
  24. head[u] = nume;
  25. }
  26. struct R{
  27. int u,v,dis;
  28. }I[maxn];
  29. bool cmp(R a,R b){
  30. return a.dis > b.dis;
  31. }
  32. int father[maxn];
  33. int findfather(int v){
  34. if(father[v] == v)return v;
  35. return father[v] = findfather(father[v]);
  36. }
  37. void Union(int a,int b){
  38. int faA = findfather(a);
  39. int faB = findfather(b);
  40. if(faA != faB)father[faA] = faB;
  41. }
  42. void buildG(){//建最小生成树
  43. for(int i = 1;i <= nr;i++){
  44. if(findfather(I[i].u) != findfather(I[i].v)){
  45. add(I[i].u,I[i].v,I[i].dis);
  46. add(I[i].v,I[i].u,I[i].dis);
  47. Union(I[i].u,I[i].v);
  48. }
  49. }
  50. }
  51. int dep[maxn],fa[maxn],wson[maxn],top[maxn],size[maxn],pos[maxn],ori[maxn];
  52. int val[maxn];
  53. int vis[maxn];
  54. void dfs1(int id,int F){
  55. vis[id] = true;
  56. numt++;
  57. size[id] = 1;
  58. for(int i = head[id];i;i = E[i].nxt){
  59. int v = E[i].v;
  60. if(v == F)continue;
  61. dep[v] = dep[id] + 1;
  62. fa[v] = id;
  63. val[v] = E[i].dis;
  64. dfs1(v,id);
  65. size[id] += size[v];
  66. if(size[v] > size[wson[id]]){
  67. wson[id] = v;
  68. }
  69. }
  70. }
  71. void dfs2(int id,int TP){
  72. top[id] = TP;
  73. pos[id] = ++cnt;
  74. ori[cnt] = id;
  75. if(!wson[id])return ;
  76. dfs2(wson[id],TP);
  77. for(int i = head[id];i;i = E[i].nxt){
  78. int v = E[i].v;
  79. if(v == fa[id] || v == wson[id])continue;
  80. dfs2(v,v);
  81. }
  82. }
  83. #define lid (id << 1)
  84. #define rid (id << 1) | 1
  85. struct sag_tree{
  86. int l,r;
  87. int min;
  88. int lazy;
  89. }tree[maxn << 2];
  90. void build(int id,int l,int r){
  91. tree[id].l = l;
  92. tree[id].r = r;
  93. if(l == r){
  94. tree[id].min = val[ori[r]];
  95. return ;
  96. }
  97. int mid = l + r >> 1;
  98. build(lid,l,mid);
  99. build(rid,mid + 1,r);
  100. tree[id].min = min(tree[lid].min,tree[rid].min);
  101. }
  102. int query(int id,int l,int r){
  103. if(tree[id].l == l && tree[id].r == r){
  104. return tree[id].min;
  105. }
  106. int mid = tree[id].l + tree[id].r >> 1;
  107. if(mid < l){
  108. return query(rid,l,r);
  109. }
  110. else if(mid >= r){
  111. return query(lid,l,r);
  112. }
  113. else{
  114. return min(query(lid,l,mid),query(rid,mid + 1,r));
  115. }
  116. }
  117. void getans(int x,int y){
  118. if(findfather(x) != findfather(y)){
  119. printf("-1\n");
  120. return ;
  121. }
  122. int ans = INF;
  123. while(top[x] != top[y]){
  124. if(dep[top[x]] < dep[top[y]])swap(x,y);
  125. ans = min(ans,query(1,pos[top[x]],pos[x]));
  126. x = fa[top[x]];
  127. }
  128. if(x == y){
  129. printf("%d\n",ans);
  130. return ;
  131. }
  132. if(dep[x] > dep[y])swap(x,y);
  133. ans = min(ans,query(1,pos[x] + 1,pos[y]));
  134. printf("%d\n",ans);
  135. }
  136. int main(){
  137. num = RD();nr = RD();
  138. for(int i = 1;i <= num;i++){
  139. father[i] = i;
  140. }
  141. for(int i = 1;i <= nr;i++){
  142. I[i].u = RD();
  143. I[i].v = RD();
  144. I[i].dis = RD();
  145. }
  146. sort(I + 1,I + 1 + nr,cmp);
  147. buildG();
  148. int s = 1;
  149. while(s <= num){
  150. if(vis[s] == false){
  151. dep[s] = 1;
  152. val[s] = INF;
  153. dfs1(s,-1);
  154. dfs2(s,s);
  155. }
  156. s++;
  157. }
  158. build(1,1,numt);
  159. na = RD();
  160. int u,v;
  161. for(int i = 1;i <= na;i++){
  162. u = RD();v = RD();
  163. getans(u,v);
  164. }
  165. return 0;
  166. }

最后,感谢大佬的帮助

大佬

广告

题解 P1967 【货车运输】的更多相关文章

  1. 题解 P1967 货车运输

    题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能 ...

  2. luogu题解P1967货车运输--树链剖分

    题目链接 https://www.luogu.org/problemnew/show/P1967 分析 NOIp的一道裸题,直接在最大生成树上剖分取最小值一下就完事了,非常好写,常数也比较小,然而题解 ...

  3. 洛谷 P1967 货车运输

    洛谷 P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在 ...

  4. P1967 货车运输

    P1967 货车运输最大生成树+lca+并查集 #include<iostream> #include<cstdio> #include<queue> #inclu ...

  5. 洛谷P3379lca,HDU2586,洛谷P1967货车运输,倍增lca,树上倍增

    倍增lca板子洛谷P3379 #include<cstdio> struct E { int to,next; }e[]; ],anc[][],log2n,deep[],n,m,s,ne; ...

  6. Luogu P1967 货车运输(Kruskal重构树)

    P1967 货车运输 题面 题目描述 \(A\) 国有 \(n\) 座城市,编号从 \(1\) 到 \(n\) ,城市之间有 \(m\) 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 \ ...

  7. 【杂题总汇】NOIP2013(洛谷P1967) 货车运输

    [洛谷P1967] 货车运输 重做NOIP提高组ing... +传送门-洛谷P1967+ ◇ 题目(copy from 洛谷) 题目描述 A国有n座城市,编号从1到n,城市之间有m条双向道路.每一条道 ...

  8. 洛谷 P1967 货车运输(克鲁斯卡尔重构树)

    题目描述 AAA国有nn n座城市,编号从 11 1到n nn,城市之间有 mmm 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 qqq 辆货车在运输货物, 司机们想知道每辆车在不超过车 ...

  9. P1967 货车运输(倍增LCA,生成树)

    题目链接: https://www.luogu.org/problemnew/show/P1967 题目描述 A国有n座城市,编号从 1到n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制, ...

  10. 洛谷 P1967 货车运输 Label: 倍增LCA && 最小瓶颈路

    题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多 ...

随机推荐

  1. 在Windows下制作静态库和动态库

    一:静态库的创建 VC++6.0中new一个的为win32 static library工程,之后有二个选项.根据需求选吧. 具体的类或者函数的添加过程和标准的工程一样,直接创建新的类或者添加新 的. ...

  2. 第二阶段Sprint冲刺会议9

    进展:查看有关“共享平台”的资料,看如何实现上传下载功能,并尝试编码,没有成功.

  3. Task 8 找水王

    任务: 三人行设计了一个灌水论坛.信息学院的学生都喜欢在上面交流灌水,传说在论坛上有一个“水王”,他不但喜欢发帖,还会回复其他ID发的每个帖子.坊间风闻该“水王”发帖数目超过了帖子数目的一半. 如果你 ...

  4. 编程之法section II: 2.1 求最小的k个数

    ====数组篇==== 2.1 求最小的k个数: 题目描述:有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 解法一: 思路:快排后输出前k个元素,O(nlogn). writer: zz ...

  5. js 杂项(一)函数篇

    你还应该知道箭头函数( => )可以用来保留上下文.这个方法也可以:

  6. angularJS中$apply()方法详解

    这篇文章主要介绍了angularJS中$apply()方法详解,需要的朋友可以参考下   对于一个在前端属于纯新手的我来说,Javascript都还是一知半解,要想直接上手angular JS,遇到的 ...

  7. Beta阶段——3

    一.提供当天站立式会议照片一张: 二. 每个人的工作 (有work item 的ID) (1) 昨天已完成的工作: 今天主要是对管理员功能进行改进,解决了Alpha阶段出现的一些问题 (2) 今天计划 ...

  8. 8th 对软件工程的理解(读构建之法有感)

    对于任何一个学计算机的人来说,软件都不陌生,甚至于一个普通的朝九晚五的上班族,他的每日生活工作也都与软件有着密不可分的关系.然而,程序又是如何从一行行指尖留下的代码,机器存储的数据变成快捷高效的软件的 ...

  9. Js apply方法详解,及其apply()方法的妙用

    Js apply方法详解 我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

  10. [转帖] InfiniBand主流厂商和产品分析

    https://blog.csdn.net/swingwang/article/details/72935461 InfiniBand主流厂商和产品分析 2017年06月08日 22:03:46 Ha ...