[学习笔记] LCA - 图论
1.[学习笔记] MST(最小生成树) - 图论
2.[学习笔记] LCA - 图论
3.[学习笔记] 树上差分 - 图论4.[学习笔记] 高斯消元 - 线性代数5.[学习笔记] 丢番图方程 & 同余 & 逆元 - 数论6.[学习笔记] 质数与唯一分解定理 - 数论7.[学习笔记] 乘性函数 - 数论8.[学习笔记] hash & kmp & Trie树 - 字符串9.[学习笔记] 单调队列优化DP - DP10.[学习笔记] 斜率优化DP - DP11.[学习笔记] 树链剖分(重链剖分) - 图论 & 数据结构12.[学习笔记] 动态开点权值线段树合并 - 数据结构13.[学习笔记] 长链剖分 - 图论14.[学习笔记] Splay & Treap 平衡树 - 数据结构最大生成树+LCA+倍增
好家伙,这道题我写了一个晚上,调了两个晚上,对于这道题我颇有感触。但这道题确实好,实实在在的蓝题,让我发现了许多关于LCA的问题。
首先,这个题给的是一个无向图,并不是个树,为了减少运算量,我们可以把它变成一个树。运用Kruskal算法生成一颗 最大生成树(即这棵树里所有的边权都是最大的)。因为我们要求经过的路径中最短的边的最大值(有点绕),所以这颗最大生成树在保证图原本的连通性的同时,也保证了边权的最大性。
其次,求树上点到点的边权最小值,可以 ,也就是分别找到 和 到 的最小边权即可。我们知道:求树上路径的长度可以用仅仅一个数组或者是树状数组,但是求一个树链上的最小边权可不是件容易的事。
这让我想到了与树链有关的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; }
没写完。
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18130150
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步