[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. FinalReference 如何使 GC 过程变得拖拖拉拉

    本文基于 OpenJDK17 进行讨论,垃圾回收器为 ZGC. 提示: 为了方便大家索引,特将在上篇文章 <以 ZGC 为例,谈一谈 JVM 是如何实现 Reference 语义的> 中讨 ...

  2. Mybatis.xml文件中 大于小于等于

    Mybatis中 大于小于等于的转义写法第一种写法:符号    转义字符<    <<=    <=>    >>=    >=&    &am ...

  3. C# DateTime日期字段转中文文字

    public static String ToChineseYearAndMonth(this DateTime dt) { string[] chineseNumbers = { "零&q ...

  4. 从github下好dirsearch后出现要下载文件依赖错误

    pip3 install -r requirements.txt

  5. k8s健康检查(探针Probe)之LivenessProbe、ReadinessProbe和StartupProbe

    背景 集群正常服务时,会出现容器死掉问题,如宿主机故障.资源不足.下游故障等.这个时候容器需要从endpoints摘除(容器挂了就不能接流了),并执行它的restart策略. LivenessProb ...

  6. RIP总结

    RIP     两种更新方式:定期更新和触发更新     管理距离为120,更新使用UDP520,更新周期30s,使用跳数作为度量值,最大15     RIP有三个版本RIPv1,RIPv2,RIPn ...

  7. GaussDB(DWS)性能调优,解决DM区大内存占用问题

    本文分享自华为云社区<GaussDB(DWS)性能调优:DM区优化案例--维度表关联条件存在会计期>,作者: O泡果奶~. 当前DM(P1.P3.CBGDM)存在维度表与主表关联时使用会计 ...

  8. Java 散列表HashTable

    什么是散列表hash table和使用场景 什么是散列表 散列表(Hash table,也叫哈希表),是根据关键码值(key value)而直接进行访问的数据结构.它通过把关键码值映射到表中一个位置来 ...

  9. yb课堂 注册-登陆-个人模块 《三十九》

    Cube-UI的form表单 https://didi.github.io/cube-ui/#/zh-CN/docs/form 注册 Register.vue <template> < ...

  10. yb课堂 新版SSM-SpringBoot2.X 后端项目框架搭建 《二》

    在线创建 https://start.spring.io/ 依赖项 Spring Boot核心包 <dependency> <groupId>org.springframewo ...