Skip to content

Commit 391da92

Browse files
committed
add 次小生成树
1 parent de9ff75 commit 391da92

File tree

3 files changed

+136
-16
lines changed

3 files changed

+136
-16
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@
187187
- Kruskal
188188
- Prim
189189
- 单度限制最小生成树
190+
- 次小生成树
190191
- 最小差值生成树
191192
- 最小树形图
192193
- 朱刘算法

copypasta/graph.go

Lines changed: 122 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,8 +1497,8 @@ func (*graph) mstKruskal(in io.Reader, n, m int) int64 {
14971497
cntE := 0
14981498
for _, e := range edges {
14991499
if fv, fw := find(e.v), find(e.w); fv != fw {
1500-
sum += int64(e.wt)
15011500
fa[fv] = fw
1501+
sum += int64(e.wt)
15021502
cntE++
15031503
}
15041504
}
@@ -1701,14 +1701,128 @@ func (*graph) limitDegreeMST(dis [][]int, root, lim int) int {
17011701
return mstSum
17021702
}
17031703

1704-
// 次小生成树 Second best Minimum Spanning Tree
1705-
// todo 非严格/严格
1706-
// Using Kruskal and Lowest Common Ancestor
1707-
// https://oi-wiki.org/graph/mst/#_9
1704+
// 严格次小生成树 Second best Minimum Spanning Tree
1705+
// https://oi-wiki.org/graph/mst/#_13
17081706
// https://cp-algorithms.com/graph/second_best_mst.html
1709-
// todo 模板题 https://www.luogu.com.cn/problem/P4180
1710-
func (*graph) secondMST(n, m int) (sum int64) {
1711-
return
1707+
// 模板题(严格)https://www.luogu.com.cn/problem/P4180 https://www.acwing.com/problem/content/358/
1708+
// 注:非严格次小生成树
1709+
// 做法更加简单,维护路径最大值即可,见 https://oi-wiki.org/graph/mst/#_10
1710+
func (*graph) strictlySecondMST(n int, edges []struct{ v, w, wt int }, min, max func(int, int) int) int {
1711+
sort.Slice(edges, func(i, j int) bool { return edges[i].wt < edges[j].wt })
1712+
1713+
fa := make([]int, n)
1714+
for i := range fa {
1715+
fa[i] = i
1716+
}
1717+
var find func(int) int
1718+
find = func(x int) int {
1719+
if fa[x] != x {
1720+
fa[x] = find(fa[x])
1721+
}
1722+
return fa[x]
1723+
}
1724+
1725+
mstSum := 0 // int64
1726+
inMST := make([]bool, len(edges))
1727+
type nb struct{ to, wt int }
1728+
g := make([][]nb, n)
1729+
for i, e := range edges {
1730+
v, w, wt := e.v, e.w, e.wt
1731+
if fv, fw := find(v), find(w); fv != fw {
1732+
fa[fv] = fw
1733+
mstSum += wt
1734+
inMST[i] = true
1735+
g[v] = append(g[v], nb{w, wt}) // MST
1736+
g[w] = append(g[w], nb{v, wt})
1737+
}
1738+
}
1739+
1740+
const mx = 17
1741+
type pair struct{ p, fi, se int }
1742+
pa := make([][mx]pair, n)
1743+
dep := make([]int, n)
1744+
var build func(v, p, d int)
1745+
build = func(v, p, d int) {
1746+
pa[v][0].p = p
1747+
dep[v] = d
1748+
for _, e := range g[v] {
1749+
if w := e.to; w != p {
1750+
pa[w][0].fi = e.wt
1751+
build(w, v, d+1)
1752+
}
1753+
}
1754+
}
1755+
build(0, -1, 0)
1756+
1757+
merge := func(xFi, xSe, yFi, ySe int) (int, int) {
1758+
fi, se := max(xFi, yFi), 0
1759+
if xFi == yFi {
1760+
se = max(xSe, ySe)
1761+
} else if xFi > yFi {
1762+
se = max(xSe, yFi)
1763+
} else {
1764+
se = max(xFi, ySe)
1765+
}
1766+
return fi, se
1767+
}
1768+
for i := 0; i+1 < mx; i++ {
1769+
for v := range pa {
1770+
if p := pa[v][i]; p.p != -1 {
1771+
pp := pa[p.p][i]
1772+
fi, se := merge(p.fi, p.se, pp.fi, pp.se)
1773+
pa[v][i+1] = pair{pp.p, fi, se}
1774+
} else {
1775+
pa[v][i+1].p = -1
1776+
}
1777+
}
1778+
}
1779+
1780+
// 返回路径最大边权和严格次大边权
1781+
queryPath := func(v, w int) (fi, se int) {
1782+
if dep[v] > dep[w] {
1783+
v, w = w, v
1784+
}
1785+
for i := 0; i < mx; i++ {
1786+
if (dep[w]-dep[v])>>i&1 > 0 {
1787+
p := pa[w][i]
1788+
fi, se = merge(fi, se, p.fi, p.se)
1789+
w = p.p
1790+
}
1791+
}
1792+
if w != v {
1793+
for i := mx - 1; i >= 0; i-- {
1794+
if pv, pw := pa[v][i], pa[w][i]; pv.p != pw.p {
1795+
fi, se = merge(fi, se, pv.fi, pv.se)
1796+
fi, se = merge(fi, se, pw.fi, pw.se)
1797+
v, w = pv.p, pw.p
1798+
}
1799+
}
1800+
fi, se = merge(fi, se, pa[v][0].fi, pa[v][0].se)
1801+
fi, se = merge(fi, se, pa[w][0].fi, pa[w][0].se)
1802+
}
1803+
return
1804+
}
1805+
1806+
const inf int = 1e9 // 1e18
1807+
delta := inf
1808+
for i, e := range edges {
1809+
v, w, wt := e.v, e.w, e.wt
1810+
if inMST[i] || v == w { // 注意跳过自环
1811+
continue
1812+
}
1813+
fi, se := queryPath(v, w)
1814+
if wt > fi {
1815+
delta = min(delta, wt-fi) // 替换从而得到更大的 MST,取最小的替换差值
1816+
} else if se > 0 { // 此时必然有 wt == fi
1817+
delta = min(delta, wt-se) // wt = fi > se,同样可以替换
1818+
}
1819+
}
1820+
if delta == inf {
1821+
return -1
1822+
}
1823+
mstSum += delta
1824+
1825+
return mstSum
17121826
}
17131827

17141828
// Kruskal 重构树

copypasta/graph_tree.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -647,10 +647,11 @@ func (*tree) numPairsWithDistanceLimit(in io.Reader, n, root int, upperDis int64
647647
// https://atcoder.jp/contests/arc060/tasks/arc060_c
648648
// 路径点权乘积 https://ac.nowcoder.com/acm/contest/6913/C
649649
// 树上倍增应用(静态路径查询):代码见下面的 EXTRA 部分
650-
// 最大值(与 MST 结合)https://codeforces.com/problemset/problem/609/E
650+
// 维护最大值(与 MST 结合)https://codeforces.com/problemset/problem/609/E
651651
// 变体 https://codeforces.com/problemset/problem/733/F
652-
// 最大值(与 MST 结合)LC1697 的在线做法 https://leetcode-cn.com/problems/checking-existence-of-edge-length-limited-paths/
653-
// 前十大(点权)https://codeforces.com/problemset/problem/587/C
652+
// 维护最大值(与 MST 结合)LC1697 的在线做法 https://leetcode-cn.com/problems/checking-existence-of-edge-length-limited-paths/
653+
// 维护最大值和严格次大值(严格次小 MST):见 graph.go 中的 strictlySecondMST
654+
// 维护前十大(点权)https://codeforces.com/problemset/problem/587/C
654655
// 树上倍增-查询深度最小的未被标记的点 https://codeforces.com/problemset/problem/980/E
655656
// 题目推荐 https://cp-algorithms.com/graph/lca.html#toc-tgt-2
656657
// todo poj2763 poj1986 poj3728
@@ -766,18 +767,19 @@ func (*tree) lcaBinarySearch(n, root int, g [][]int) {
766767
type pair struct{ p, maxWt int }
767768
pa := make([][mx]pair, n)
768769
dep := make([]int, n)
769-
var f func(v, p, d int)
770-
f = func(v, p, d int) {
770+
var build func(v, p, d int)
771+
build = func(v, p, d int) {
771772
pa[v][0].p = p
772773
dep[v] = d
773774
for _, e := range g[v] {
774775
if w := e.to; w != p {
775776
pa[w][0].maxWt = e.wt
776-
f(w, v, d+1)
777+
build(w, v, d+1)
777778
}
778779
}
779780
}
780-
f(0, -1, 0)
781+
build(0, -1, 0)
782+
781783
for i := 0; i+1 < mx; i++ {
782784
for v := range pa {
783785
if p := pa[v][i]; p.p != -1 {
@@ -788,6 +790,7 @@ func (*tree) lcaBinarySearch(n, root int, g [][]int) {
788790
}
789791
}
790792
}
793+
791794
// 求 LCA(v,w) 的同时,顺带求出 v-w 上的边权最值
792795
_lca := func(v, w int) (lca, maxWt int) {
793796
if dep[v] > dep[w] {
@@ -811,8 +814,10 @@ func (*tree) lcaBinarySearch(n, root int, g [][]int) {
811814
v = pa[v][0].p
812815
}
813816
// 如果是点权的话这里加上 maxWt = max(maxWt, pa[v][0].maxWt)
814-
return v, maxWt
817+
lca = v
818+
return
815819
}
820+
816821
_ = _lca
817822
}
818823

0 commit comments

Comments
 (0)