博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
题解 【luoguP1967 NOIp提高组2013 货车运输】
阅读量:4970 次
发布时间:2019-06-12

本文共 2704 字,大约阅读时间需要 9 分钟。


题解

题意

  • 给你一个无向图,求两个点之间的一条路径,使路径上的最小值最大

算法:Kruskal最大生成树+倍增lca

分析

  • 首先容易知道,答案一定在该图的最大生成树
  • 之后问题便转换成了树上点\(u\)\(v\)的简单路径42中最小的边权
  • 经典的树上倍增
  • 用fa[i][j]来表示从第\(i\)个点往上\(2^j\)条边到达的点
  • 用s[i][j]来表示从第\(i\)个点往上\(2^j\)条边中的最小值
  • 答案就是在求lca的过程中统计一下最小值就可以了(具体详见代码)

复杂度

Kruskal \(O(m \log m)\)
倍增 \(O(n \log n)\)
总复杂度 \(O(n \log n + m \log m)\)
代码

#include 
#include
#include
#include
using namespace std;const int MAXN = 50050;int n, m, q;int p[MAXN];int dep[MAXN], vis[MAXN], fa[MAXN][20], s[MAXN][20]; struct Edge{ int u, v, w; bool operator < (const Edge &_edge) const { return w > _edge.w; }}edge[MAXN];int ecnt;struct node{ int v, w; node *next;}pool[MAXN], *head[MAXN];void addedge(int u, int v, int w){ node *p = &pool[++ecnt], *q = &pool[++ecnt]; p->v = v, p->w = w, p->next = head[u], head[u] = p; q->v = u, q->w = w, q->next = head[v], head[v] = q;}int find(int x){ if(p[x] == 0) return x; else return p[x] = find(p[x]);}bool Union(int x, int y){ int px = find(x); int py = find(y); if(px == py) return false; p[px] = py; return true;}void dfs(int u){ int v; vis[u] = 1; for(node *p = head[u]; p; p = p->next) if(!vis[v = p->v]) { dep[v] = dep[u] + 1; fa[v][0] = u; s[v][0] = p->w; for(int j = 1; fa[v][j - 1] != 0; j++) fa[v][j] = fa[fa[v][j - 1]][j - 1], s[v][j] = min(s[v][j - 1], s[fa[v][j - 1]][j - 1]); //预处理 dfs(v); }}int query(int u, int v)//倍增{ int ret = 2147483647;//返回值。因为求最小,所以初始赋最大 if(dep[u] < dep[v]) swap(u, v); for(int i = 15; i >= 0; i--) if(fa[u][i] != 0 && dep[fa[u][i]] >= dep[v]) ret = min(ret, s[u][i]), u = fa[u][i];//一定要先统计最小值 if(u == v) return ret; for(int i = 15; i >= 0; i--) if(fa[u][i] != fa[v][i]) ret = min(ret, min(s[u][i], s[v][i])), //统计最小 u = fa[u][i], v = fa[v][i]; ret = min(ret, min(s[u][0], s[v][0]));//不要忘了最后还有两条边 return ret;}int main(){ scanf("%d%d", &n, &m); for(int i = 1; i <= m; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w); sort(edge + 1, edge + m + 1); for(int i = 1; i <= m; i++) if(Union(edge[i].u, edge[i].v)) addedge(edge[i].u, edge[i].v, edge[i].w); dfs(1); scanf("%d", &q); for(int i = 1; i <= q; i++) { int u, v; scanf("%d%d", &u, &v); if(find(u) != find(v)) { printf("-1\n"); continue ; } printf("%d\n", query(u, v)); } return 0;}

转载于:https://www.cnblogs.com/acfunction/p/8857094.html

你可能感兴趣的文章
[转载]java开发中的23种设计模式
查看>>
表格的拖拽功能
查看>>
函数的形参和实参
查看>>
【TP SRM 703 div2 500】 GCDGraph
查看>>
webdriver api
查看>>
apache 实现图标缓存客户端
查看>>
揭秘:黑客必备的Kali Linux是什么,有哪些弊端?
查看>>
linux系统的远程控制方法——学神IT教育
查看>>
springboot+mybatis报错Invalid bound statement (not found)
查看>>
Linux环境下SolrCloud集群环境搭建关键步骤
查看>>
P3565 [POI2014]HOT-Hotels
查看>>
MongoDB的简单使用
查看>>
prometheus配置
查看>>
【noip2004】虫食算——剪枝DFS
查看>>
python 多进程和多线程的区别
查看>>
sigar
查看>>
iOS7自定义statusbar和navigationbar的若干问题
查看>>
[Locked] Wiggle Sort
查看>>
deque
查看>>
c#中从string数组转换到int数组
查看>>