Skip to content

Commit c6a9e44

Browse files
committed
minimum diameter spanning tree test
1 parent 23d43a1 commit c6a9e44

File tree

5 files changed

+99
-30
lines changed

5 files changed

+99
-30
lines changed

cp-algo/graph/ascending_dfs.hpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@
22
#define CP_ALGO_GRAPH_ASCENDING_DFS_HPP
33

44
#include "base.hpp"
5+
#include <cassert>
56
#include <vector>
67
#include <ranges>
78

89
namespace cp_algo::graph {
910
// Generic ascending DFS that repeatedly peels leaves (degree 1).
1011
// `next(v)` must return the unique incident edge of `v` when degree[v] == 1.
1112
template<undirected_graph_type graph>
12-
void ascending_dfs(graph const& tree, auto &&next, auto &&callback, node_index root = 0) {
13-
std::vector<int> degree(tree.n());
14-
for (auto v : tree.nodes()) {
15-
degree[v] = (int)std::ranges::distance(tree.outgoing(v));
16-
}
17-
degree[root] = 0;
13+
void ascending_dfs(graph const& tree, auto &degree, auto &&next, auto &&callback, node_index root) {
1814
for (auto v : tree.nodes()) {
1915
while (degree[v] == 1) {
2016
edge_index ep = next(v);
@@ -30,13 +26,18 @@ namespace cp_algo::graph {
3026
template<undirected_graph_type graph>
3127
auto xor_dfs(graph const& tree, auto &&callback, node_index root = 0) {
3228
std::vector<edge_index> neig_xor(tree.n());
29+
std::vector<int> degree(tree.n());
30+
for (auto v : tree.nodes()) {
31+
degree[v] = (int)std::ranges::distance(tree.outgoing(v));
32+
}
33+
degree[root] = 0;
3334
for (auto v : tree.nodes()) {
3435
for (auto e : tree.outgoing(v)) {
3536
neig_xor[v] ^= e;
3637
}
3738
}
3839
neig_xor[root] ^= edge_index(-1);
39-
ascending_dfs(tree, [&](auto v) {
40+
ascending_dfs(tree, degree, [&](auto v) {
4041
edge_index ep = neig_xor[v];
4142
neig_xor[tree.edge(ep).traverse(v)] ^= ep;
4243
return ep;
@@ -46,8 +47,20 @@ namespace cp_algo::graph {
4647
}
4748
// DFS that uses a precomputed parent-edge array.
4849
template<undirected_graph_type graph>
49-
void parent_dfs(graph const& tree, std::vector<edge_index> const& parent, auto &&callback, node_index root = 0) {
50-
ascending_dfs(tree, [&](auto v) {
50+
void parent_dfs(graph const& tree, std::vector<edge_index> const& parent, auto &&callback) {
51+
std::vector<int> degree(tree.n());
52+
node_index root = -1;
53+
for (auto [v, e] : parent | std::views::enumerate) {
54+
if (e != -1) {
55+
degree[v]++;
56+
degree[tree.edge(e).traverse(node_index(v))]++;
57+
} else {
58+
root = node_index(v);
59+
}
60+
}
61+
assert(root != -1);
62+
degree[root] = 0;
63+
ascending_dfs(tree, degree, [&](auto v) {
5164
return parent[v];
5265
}, callback, root);
5366
}

cp-algo/graph/shortest_path.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ namespace cp_algo::graph {
105105
Context sssp_impl(graph const& g, node_index s) {
106106
Context context(g.n());
107107
context.dist[s] = 0;
108+
context.pre[s] = -1;
108109
context.push(s, -1, s);
109110
while(auto ov = context.next_node()) {
110111
node_index v = *ov;

cp-algo/graph/tree_diameter.hpp

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
#include <tuple>
66
#include <string>
77
#include <algorithm>
8-
#include <memory>
98

109
namespace cp_algo::graph {
11-
enum diameter_mode { recover_path, no_recover };
12-
template<diameter_mode mode = no_recover, weighted_undirected_graph_type graph>
13-
auto tree_diameter(graph const& g, std::shared_ptr<const std::vector<edge_index>> parents = nullptr) {
10+
enum class diameter_mode { recover_path, no_recover };
11+
12+
template<diameter_mode mode = diameter_mode::no_recover, weighted_undirected_graph_type graph>
13+
auto tree_diameter(graph const& g, const std::vector<edge_index>* parents = nullptr) {
1414
struct up_path {
1515
int64_t length = 0;
1616
node_index start = 0;
@@ -32,28 +32,31 @@ namespace cp_algo::graph {
3232
up[u] = up[v];
3333
}
3434
};
35+
std::vector<edge_index> parents_owned;
3536
if (parents) {
3637
parent_dfs(g, *parents, callback);
3738
} else {
38-
parents = std::make_shared<std::vector<edge_index>>(xor_dfs(g, callback));
39+
parents_owned = xor_dfs(g, callback);
40+
parents = &parents_owned;
3941
}
40-
if constexpr (mode == no_recover) {
42+
if constexpr (mode == diameter_mode::no_recover) {
4143
return s.length + t.length;
44+
} else {
45+
auto collect = [&](up_path v) {
46+
std::basic_string<edge_index> path;
47+
while(v.length) {
48+
edge_index ep = (*parents)[v.start];
49+
path.push_back(ep);
50+
v.length -= g.edge(ep).w;
51+
v.start = g.edge(ep).traverse(v.start);
52+
}
53+
return path;
54+
};
55+
auto paths = collect(s);
56+
auto patht = collect(t);
57+
std::ranges::reverse(patht);
58+
return std::tuple{s.length + t.length, s.start, paths += patht};
4259
}
43-
auto collect = [&](up_path v) {
44-
std::basic_string<edge_index> path;
45-
while(v.length) {
46-
edge_index ep = (*parents)[v.start];
47-
path.push_back(ep);
48-
v.length -= g.edge(ep).w;
49-
v.start = g.edge(ep).traverse(v.start);
50-
}
51-
return path;
52-
};
53-
auto paths = collect(s);
54-
auto patht = collect(t);
55-
std::ranges::reverse(patht);
56-
return std::tuple{s.length + t.length, s.start, paths += patht};
5760
}
5861
}
5962
#endif // CP_ALGO_GRAPH_TREE_DIAMETER_HPP
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// @brief Minimum Diameter Spanning Tree
2+
#define PROBLEM "https://judge.yosupo.jp/problem/minimum_diameter_spanning_tree"
3+
#pragma GCC optimize("Ofast,unroll-loops")
4+
#include <iostream>
5+
#include "blazingio/blazingio.min.hpp"
6+
#include "cp-algo/graph/shortest_path.hpp"
7+
#include "cp-algo/graph/tree_diameter.hpp"
8+
#include <bits/stdc++.h>
9+
10+
using namespace std;
11+
using namespace cp_algo::graph;
12+
13+
template<weighted_undirected_graph_type graph>
14+
auto minimum_diameter_spanning_tree(graph const& g) {
15+
int64_t min_diameter = shortest_path_context::inf;
16+
std::vector<edge_index> best_edges;
17+
for(auto v: g.nodes()) {
18+
auto [_, p] = dijkstra(g, v);
19+
auto d = tree_diameter(g, &p);
20+
if (d < min_diameter) {
21+
min_diameter = d;
22+
best_edges = p;
23+
}
24+
}
25+
return std::pair{min_diameter, best_edges};
26+
}
27+
28+
void solve() {
29+
int n, m;
30+
cin >> n >> m;
31+
weighted_graph g(n);
32+
g.read_edges(m);
33+
auto [X, E] = minimum_diameter_spanning_tree(g);
34+
cout << X << '\n';
35+
for(auto e: E) {
36+
if (e != -1) {
37+
cout << e << " ";
38+
}
39+
}
40+
cout << "\n";
41+
}
42+
43+
signed main() {
44+
//freopen("input.txt", "r", stdin);
45+
ios::sync_with_stdio(0);
46+
cin.tie(0);
47+
int t = 1;
48+
//cin >> t;
49+
while(t--) {
50+
solve();
51+
}
52+
}

verify/graph/tree_diameter.test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ void solve() {
1414
cin >> n;
1515
weighted_graph g(n);
1616
g.read_edges(n - 1);
17-
auto [d, v, path] = tree_diameter<recover_path>(g);
17+
auto [d, v, path] = tree_diameter<diameter_mode::recover_path>(g);
1818
cout << d << ' ' << size(path) + 1 << '\n';
1919
cout << v;
2020
for(auto e: path) {

0 commit comments

Comments
 (0)