Java教程

[NOI2018] 归程,Kruskal 重构树

本文主要是介绍[NOI2018] 归程,Kruskal 重构树,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

给出一张点数为 \(n\),边数为 \(m\) 的无向连通图,每个边 \(e\) 的属性是一个二元组 \((l,a)\)。

接下来给出 \(q\) 次询问,每次给出一个出发点 \(v\) 以及约束 \(p\),求出从 \(v\) 至 \(1\) 号节点的最小花费。

花费的计算是这样的:将 \(p(v,1)\) 分为两段 \(p(v,u)\),\(p(u,1)\)。

\(p(v,u)\) 满足所有的经过边 \(e\in p(v,u)\) 都有 \(a_e > p\),这段路径是免费的。

从第一次经过 \(a_e\leq p\) 的边开始,这一段路径为 \(p(u,1)\),这段路径的花费是 \(\sum_{e\in p(u,1)}l_e\)。

假若询问离线,我们可以考虑将询问按照 \(p\) 从大到小排序,然后将图中的边按照 \(a\) 排序,开始是一个仅有点没有边的图,然后逐渐按顺序向其中加边,当当前询问的约束为 \(p\) 时,将所有 \(a>p\) 的边加入其中。

那么这个过程中整个图就会形成若干个连通块,假若当前起点 \(v\) 属于某个连通块 \(B\),那么任何以 \(u\in B\) 为起点的答案都是一样的。因为任意边 \(e\in B\) 都有 \(a_e>p\),是属于第一种路径,所以这部分路径是对答案没有贡献的。

于是我们考虑先预处理出所有点至 \(1\) 号节点的 \(l\) 值的最短路,这个可以直接以 \(1\) 号节点为起点跑一遍 Dijkstra(关于 SPFA,,,

然后关于连通块我们就可以直接用并查集维护,按照最短路的值合并。具体地说,就是以最短路值小的节点作为根,于是当我们处理询问时只需 find 一下根,再输出其最短路值即可。

离线的好处是,由于我们已经对询问排序,加入的边不会再被去掉,这样就可以直接用并查集维护了。时间复杂度 \(O( m\log n+q\log n)\)。

这篇关于[NOI2018] 归程,Kruskal 重构树的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!