[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. http请求方式-OkHttpClient

    http请求方式-OkHttpClient import com.example.core.mydemo.http.OrderReqVO; import okhttp3.*; import org.s ...

  2. 一些常见功能的查询sql

    Tips:当你看到这个提示的时候,说明当前的文章是由原emlog博客系统搬迁至此的,文章发布时间已过于久远,编排和内容不一定完整,还请谅解` 一些常见功能的查询sql 日期:2019-4-10 阿珏 ...

  3. 使用vue-contextmenujs鼠标右键菜单时,当高度不够时显示不全的问题

    之前是采用npm或者yarn直接装包vue-contextmenujs的形式: npm install vue-contextmenujs -S || yarn add vue-contextmenu ...

  4. 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 ...

  5. Linux 内核:设备树 学习总结

    背景 之前写过设备树DTS 学习:学习总结(应用篇)的学习,但是是偏向于应用:这次针对了设备树的架构以及在驱动中的使用流程做了补充. 基于 Linux 内核 v4.14. 目录 标题 说明 设备树:d ...

  6. 高通LCD开发常见问题&分析

    reference : https://blog.csdn.net/sinat_34606064/article/details/77921323 https://www.cnblogs.com/bi ...

  7. Taro 滚动切换tab页

    import React, { Component } from 'react' import { View, Text, ScrollView } from '@tarojs/components' ...

  8. P6655 题解

    直接计数其实不好记,不如计数转期望. 令 \(f_i\) 表示点 \(i\) 成为制高点概率,不难发现期望就是 \(\sum f_i\). 根据定义对于 \(f\) 我们有如下转移 \(f_i = \ ...

  9. Uipath学习(一)

    Uipath 常用程序包总结: (1)正则表达式依赖程序包: System.Text.RegularExpressions 用法: 这个语句的作用是判断"溢达集团"这一串字符是否为 ...

  10. axios和AJAX的区别

    axios和ajax的区别 Axios和Ajax都是用于处理网络请求和与服务器进行通信的技术,但它们之间存在一些关键的区别:12 一.技术基础:Ajax(Asynchronous JavaScript ...