[NOIP2013 提高组] 货车运输

最大生成树+LCA+倍增

好家伙,这道题我写了一个晚上,调了两个晚上,对于这道题我颇有感触。但这道题确实好,实实在在的蓝题,让我发现了许多关于LCA的问题。

首先,这个题给的是一个无向图,并不是个树,为了减少运算量,我们可以把它变成一个树。运用Kruskal算法生成一颗 最大生成树(即这棵树里所有的边权都是最大的)。因为我们要求经过的路径中最短的边的最大值(有点绕),所以这颗最大生成树在保证图原本的连通性的同时,也保证了边权的最大性。

其次,求树上点到点的边权最小值,可以 \(wg[a-b]=min(wg[a-lca], wg[b-lca])\) ,也就是分别找到 \(a\) 和 \(b\) 到 \(lca\) 的最小边权即可。我们知道:求树上路径的长度可以用仅仅一个数组或者是树状数组,但是求一个树链上的最小边权可不是件容易的事。

这让我想到了与树链有关的LCA算法:倍增法。原理就是让 \(a\) 和 \(b\) 不断沿着树链向上跳,最终找到 \(lca\) 。同理,我们也可以让wg数组跟着fa数组一块儿向上跳,最后在统计答案的时候用上即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 1, M = 5e4 + 1;
int n, m, q, f[N], cnt, h[N], fa[21][N], wg[21][N], deep[N], scc[N], lg[N];
bitset<N> flag;
struct Edge{ int u, v, dt; }E[M];
struct edge{ int v, nt, dt; }e[N];
bool cmp(Edge a, Edge b){ return a.dt > b.dt; }
inline void add(int u, int v, int dt){ e[++cnt] = (edge){v, h[u], dt}; h[u] = cnt;}
inline int find(int k){
if(!f[k]) return k;
return f[k] = find(f[k]);
}
inline void kruskal(){
sort(E+1, E+m+1, cmp);
int eg = 0;
for(int i=1; i<=m; ++i){
if(eg == n-1) break;
int fa = find(E[i].u), fb = find(E[i].v);
if(fa == fb) continue;
else{
++eg;
f[fa] = fb;
add(E[i].u, E[i].v, E[i].dt), add(E[i].v, E[i].u, E[i].dt);
}
}
}
inline void dfs(int k, int f){
scc[k] = scc[f];
flag[k] = 1;
deep[k] = deep[f] + 1;
for(int i=h[k]; i; i=e[i].nt){
int v = e[i].v;
if(!flag[v]){
wg[0][v] = e[i].dt;
fa[0][v] = k;
dfs(v, k);
// for(int t=1; t<=lg[deep[v]]; ++t){
// fa[t][v] = fa[t-1][fa[t-1][v]];
// wg[t][v] = min(wg[t-1][v], wg[t-1][fa[t-1][v]]);
// }
}
}
}
inline int getans(int a, int b){
int ans = INT_MAX;
if(deep[a] < deep[b]) swap(a, b);
// for(int i=20; i>=0; --i){
// if(deep[fa[i][a]] >= deep[b]){
// ans = min(ans, wg[i][a]);
// a = fa[i][a];
// }
// }
while(deep[a] != deep[b]){
ans = min(ans, wg[lg[deep[a]-deep[b]]][a]);
a = fa[lg[deep[a]-deep[b]]][a];
}
if(a == b) return ans;
for(int i=lg[deep[a]]; i>=0; --i)
if(fa[i][a] != fa[i][b]){
ans = min({ans, wg[i][a], wg[i][b]});
a = fa[i][a], b = fa[i][b];
}
return min({ans, wg[0][a], wg[0][b]});
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>m;
lg[0] = -1;
for(int i=1; i<=n; ++i) lg[i] = lg[i>>1] + 1;
for(int i=1; i<=m; ++i) cin>>E[i].u>>E[i].v>>E[i].dt;
kruskal();
deep[0] = -1;
for(int i=1; i<=n; ++i){
if(!flag[i]){
++scc[0];
dfs(i, 0);
fa[0][i] = i;
wg[0][i] = INT_MAX;
}
}
// for(int j=1; j<=n; ++j){
// for(int i=1; i<=20; ++i){
// fa[i][j] = fa[i-1][fa[i-1][j]];
// wg[i][j] = min(wg[i-1][j], wg[i-1][fa[i-1][j]]);
// }
// }
for(int i=1; i<=20; ++i){
for(int j=1; j<=n; ++j){
fa[i][j] = fa[i-1][fa[i-1][j]];
wg[i][j] = min(wg[i-1][j], wg[i-1][fa[i-1][j]]);
}
}
cin>>q;
for(int i=1, a, b; i<=q; ++i){
cin>>a>>b;
if(scc[a] != scc[b]) cout<<"-1\n";
else cout<<getans(a, b)<<'\n';
}
return 0;
}

没写完。

[学习笔记] LCA - 图论的更多相关文章

  1. Day 4 学习笔记 各种图论

    Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...

  2. 图论学习笔记·$Floyd$ $Warshall$

    对于图论--虽然本蒟蒻也才入门--于是有了这篇学习笔记\(qwq\) 一般我们对于最短路的处理,本蒟蒻之前都是通过构建二维数组的方式然后对每两个点进行1次深度或者广度优先搜索,即一共进行\(n\)^2 ...

  3. 算法学习笔记(5): 最近公共祖先(LCA)

    最近公共祖先(LCA) 目录 最近公共祖先(LCA) 定义 求法 方法一:树上倍增 朴素算法 复杂度分析 方法二:dfs序与ST表 初始化与查询 复杂度分析 方法三:树链剖分 DFS序 性质 重链 重 ...

  4. 图论 竞赛图(tournament)学习笔记

    竞赛图(tournament)学习笔记 现在只是知道几个简单的性质... 竞赛图也叫有向完全图. 其实就是无向完全图的边有了方向. ​ 有一个很有趣的性质就是:一个tournament要么没有环,如果 ...

  5. OI知识点|NOIP考点|省选考点|教程与学习笔记合集

    点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...

  6. kruskal重构树学习笔记

    \(kruskal\) 重构树学习笔记 前言 \(8102IONCC\) 中考到了,本蒟蒻不会,所以学一下. 前置知识 \(kruskal​\) 求最小(大)生成树,树上求 \(lca​\). 算法详 ...

  7. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  8. 树上启发式合并(dsu on tree)学习笔记

    有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...

  9. 「学习笔记」wqs二分/dp凸优化

    [学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...

  10. [学习笔记]可持久化数据结构——数组、并查集、平衡树、Trie树

    可持久化:支持查询历史版本和在历史版本上修改 可持久化数组 主席树做即可. [模板]可持久化数组(可持久化线段树/平衡树) 可持久化并查集 可持久化并查集 主席树做即可. 要按秩合并.(路径压缩每次建 ...

随机推荐

  1. 面试官:Java中缓冲流真的性能很好吗?我看未必

    一.写在开头 上一篇文章中,我们介绍了Java IO流中的4个基类:InputStream.OutputStream.Reader.Writer,那么这一篇中,我们将以四个基类所衍生出来,应对不同场景 ...

  2. parsel的使用

    介绍 parsel这个库可以解析HTML和XML,并支持使用XPath和CSS选择器对内容进行提取和修改,同时还融合了正则表达式的提取功能.parsel灵活强大,同时也是Python最流行的爬虫框架的 ...

  3. 基于NXP i.MX 6ULL——MQTT通信协议的开发案例

    前  言 本指导文档适用开发环境: Windows开发环境:Windows 7 64bit.Windows 10 64bit Linux开发环境:Ubuntu 18.04.4 64bit 拟机:VMw ...

  4. vba--数组

    Sub shishi() Range("e2") = Split(Range("e1"), "-")(0) '用短横线分隔后取第1个值 En ...

  5. MAPREDUCE中的序列化

    Java的序列化是一个重量级序列化框架(Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息,header,继承体系....),不便于在网络中高效传输: 所以,hadoo ...

  6. 一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案

    前言 今天大姚给大家分享一款EF Core下高性能.轻量级针对分表分库读写分离的解决方案,开源(Apache License)的EF Core拓展程序包:ShardingCore. ShardingC ...

  7. yb课堂 ECMAScript 6常见语法快速入门 《三十一》

    什么是ES 6 ECMAScript 6(简称ES6)是于2015年6月正式发布的JavaScript语言的标准,正式名为ECMAScript 2015(ES2015).它的目标是使用JavaScri ...

  8. Microsoft宣布将在开发人员会议上专注于.NET Aspire

    2024年7月15日微软宣布,其开发执行团队将在下个月的开发者大会上聚焦于使用 .NET Aspire 的云原生开发,以及结合人工智能的"现代 SQL"在 Microsoft Fa ...

  9. Error: Dynamic require of "path" is not supported

    failed to load config from D:\BaiduSyncdisk\vue3\sys-manager\vite.config.jserror when starting dev s ...

  10. [oeasy]python0091_仙童公司_八叛逆_intel_8080_altair8800_牛郎星

    编码进化 个人电脑 计算机 通过电话网络 进行连接 极客 利用技术 做一些有趣的尝试 极客文化 是 认真研究技术的 文化 计算机 不再是 高校和研究机构高墙里面的 神秘事物 而是 生活中常见的 家用电 ...