[学习笔记] LCA - 图论
最大生成树+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 - 图论的更多相关文章
- Day 4 学习笔记 各种图论
Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...
- 图论学习笔记·$Floyd$ $Warshall$
对于图论--虽然本蒟蒻也才入门--于是有了这篇学习笔记\(qwq\) 一般我们对于最短路的处理,本蒟蒻之前都是通过构建二维数组的方式然后对每两个点进行1次深度或者广度优先搜索,即一共进行\(n\)^2 ...
- 算法学习笔记(5): 最近公共祖先(LCA)
最近公共祖先(LCA) 目录 最近公共祖先(LCA) 定义 求法 方法一:树上倍增 朴素算法 复杂度分析 方法二:dfs序与ST表 初始化与查询 复杂度分析 方法三:树链剖分 DFS序 性质 重链 重 ...
- 图论 竞赛图(tournament)学习笔记
竞赛图(tournament)学习笔记 现在只是知道几个简单的性质... 竞赛图也叫有向完全图. 其实就是无向完全图的边有了方向. 有一个很有趣的性质就是:一个tournament要么没有环,如果 ...
- OI知识点|NOIP考点|省选考点|教程与学习笔记合集
点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...
- kruskal重构树学习笔记
\(kruskal\) 重构树学习笔记 前言 \(8102IONCC\) 中考到了,本蒟蒻不会,所以学一下. 前置知识 \(kruskal\) 求最小(大)生成树,树上求 \(lca\). 算法详 ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- 树上启发式合并(dsu on tree)学习笔记
有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...
- 「学习笔记」wqs二分/dp凸优化
[学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...
- [学习笔记]可持久化数据结构——数组、并查集、平衡树、Trie树
可持久化:支持查询历史版本和在历史版本上修改 可持久化数组 主席树做即可. [模板]可持久化数组(可持久化线段树/平衡树) 可持久化并查集 可持久化并查集 主席树做即可. 要按秩合并.(路径压缩每次建 ...
随机推荐
- 如何判断APP页面是原生还是H5
如何判断APP页面是原生还是H5 1.打开设置,搜索"开发者选项",点击"开发者选项" 华为手机进入开发者模式方法 1.打开华为手机的[设置],找到并点击进入[ ...
- MAC10.12Caps Lock失灵
先说一下小弟的MAC系统是黑苹果来的,笔记本并没有那个显示大小写的指示灯,所以一开始的时候一直以为自己的键盘坏了还特意换了一个(结果质量比原来的更差),输入密码因为有大小写经常被提示密码错误所以蛋疼得 ...
- 解析下载blob视频
前言 浏览器中有些视频是通过blob:https://baike.baidu.com/bf834217-9442-4c98-9ef6-0bd5f3408a4e的形式给出的.blob后面的网址不能直接访 ...
- python 第一次主要是if while 奇偶数的练习
例如输出1-10的数字,但是7除外. a=1 while a < 11: if a == 7: pass else: print(a) a=a+1 输出1-100所以的偶数 a=1 while ...
- shell 根据 指定列 进行 去除 重复行
根据指定列进行去除重复行 这里的重复是指如果两行的某一列数据相同,则认为是重复数据. 例如:第1行与第2行数据,其中的第2列(以- 作为分隔符)明显是重复的. 100069 - ARM Compile ...
- OPC 数据采集 解决方案
笔者计划从此篇博客开始,详细介绍OPC数据采集采集过程.包括常用组态软件介绍,数据接入,OPC接入过程,常用OPC数据接入与处理全流程范例,分享相关案例Demo. 因为分享的都是个人实际工作经验中的 ...
- 好消息!数据库管理神器 Navicat 推出免费精简版:Navicat Premium Lite
前言 好消息,前不久Navicat推出了免费精简版的数据库管理工具Navicat Premium Lite,可用于商业和非商业目的,我们再也不需要付费.找破解版或者找其他免费平替工具了,有需要的同学可 ...
- Mac Docker设置国内镜像加速器
安装docker 点我直达 设置国内加速镜像 { "experimental": false, "features": { "buildkit&quo ...
- 使用ES6中Class实现手写PromiseA+,完美通过官方872条用例
目录 Promise出现的原因 myPromise的实现要点 myPromise的实现 myPromise - 实现简单的同步 myPromise - 增加异步功能 myPromise - 链式调用( ...
- [oeasy]python0075_删除变量_del_delete_variable
删除变量 回忆上次内容 上次我们研究了字节序 字节序有两种 符号 英文名称 中文名称 < little-endian 小字节序 > big-endian 大字节序 字节序 用来 明确 ...