tree_diameter.hpp¶
求带权或无权树的直径端点、长度与路径;适合树上最远距离和中心类问题。
Complexity: Time: O(n) for a tree diameter; O(1) diameter-monoid merge. Space: O(n).
AC 记录:tree_diameter。
Implementation¶
当前头文件,省略 include guard;依赖见 #include。
/// @complexity Time: O(n) for a tree diameter; O(1) diameter-monoid merge.
/// Space: O(n).
#include "noya/graph/lowest_common_ancestor.hpp"
#include "noya/graph/shortest_path.hpp"
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <tuple>
#include <utility>
#include <vector>
namespace noya {
/// @brief Unweighted tree diameter via double BFS. @return (diameter, u, v, eccentricity[]).
inline std::tuple<int, int, int, std::vector<int>>
tree_diam(std::vector<std::vector<int>> &g) {
if (g.empty())
return {-1, -1, -1, {}};
auto d0 = bfs_unweighted(g, 0).first;
int p = std::max_element(d0.begin(), d0.end()) - d0.begin();
auto dp = bfs_unweighted(g, p).first;
int q = std::max_element(dp.begin(), dp.end()) - dp.begin();
auto dq = bfs_unweighted(g, q).first;
int n = int(g.size());
std::vector<int> ecc(n);
for (int i = 0; i < n; i++)
ecc[i] = std::max(dp[i], dq[i]);
return {dp[q], p, q, ecc};
}
/// @brief Weighted tree diameter and one realizing vertex path.
/// @return (distance, path from one endpoint to the other).
template <class Weight>
std::pair<Weight, std::vector<int>> weighted_tree_diameter(
const std::vector<std::vector<std::pair<int, Weight>>> &G) {
if (G.empty()) {
return {Weight{}, {}};
}
auto dfs = [&](int s) {
std::vector<Weight> dis(G.size());
std::vector<int> fa(G.size(), -1);
std::vector<int> stk = {s};
fa[s] = s;
for (int idx = 0; idx < int(stk.size()); idx++) {
int u = stk[idx];
for (auto [nxt, w] : G[u]) {
if (fa[nxt] != -1) {
continue;
}
fa[nxt] = u;
dis[nxt] = dis[u] + w;
stk.push_back(nxt);
}
}
int far = s;
for (int u = 0; u < int(G.size()); u++) {
if (dis[far] < dis[u]) {
far = u;
}
}
return std::tuple{far, std::move(dis), std::move(fa)};
};
auto [a1, id1, ip] = dfs(0);
auto [b1, dis, fa] = dfs(a1);
std::vector<int> pth;
for (int u = b1;; u = fa[u]) {
pth.push_back(u);
if (u == a1) {
break;
}
}
std::reverse(pth.begin(), pth.end());
return {dis[b1], pth};
}
/// @brief Diameter monoid for segment tree. Merge two vertex sets and track the farthest pair.
struct diameter_monoid {
using value_type = std::pair<int64_t, std::array<int, 2>>;
using S = value_type;
using X = value_type;
static constexpr value_type identity = {-1, {-1, -1}};
static constexpr bool commute = true;
diameter_monoid() = default;
explicit diameter_monoid(const fastlca &l) { set_lca(l); }
static const fastlca *&get_lca() {
static const fastlca *lca = nullptr;
return lca;
}
static void set_lca(const fastlca &l) { get_lca() = &l; }
static value_type unit() { return identity; }
static value_type e() { return unit(); }
static value_type make(int v) { return {0, {v, v}}; }
static value_type from_vertex(int v) { return make(v); }
static value_type op(value_type a, value_type b) {
if (a == unit()) return b;
if (b == unit()) return a;
const fastlca *lca = get_lca();
assert(lca != nullptr);
value_type c = std::max(a, b);
for (auto x : a.second)
for (auto y : b.second) {
int64_t d = lca->distance(x, y);
if (d > c.first)
c = {d, {x, y}};
}
return c;
}
static value_type merge(value_type a, value_type b) { return op(a, b); }
};
} // namespace noya
// NOYA_TREE_DIAMETER_HPP
#ifndef NOYA_TREE_DIAMETER_HPP
#define NOYA_TREE_DIAMETER_HPP 1
/// @complexity Time: O(n) for a tree diameter; O(1) diameter-monoid merge.
/// Space: O(n).
#include "noya/graph/lowest_common_ancestor.hpp"
#include "noya/graph/shortest_path.hpp"
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <tuple>
#include <utility>
#include <vector>
namespace noya {
/// @brief Unweighted tree diameter via double BFS. @return (diameter, u, v, eccentricity[]).
inline std::tuple<int, int, int, std::vector<int>>
tree_diam(std::vector<std::vector<int>> &g) {
if (g.empty())
return {-1, -1, -1, {}};
auto d0 = bfs_unweighted(g, 0).first;
int p = std::max_element(d0.begin(), d0.end()) - d0.begin();
auto dp = bfs_unweighted(g, p).first;
int q = std::max_element(dp.begin(), dp.end()) - dp.begin();
auto dq = bfs_unweighted(g, q).first;
int n = int(g.size());
std::vector<int> ecc(n);
for (int i = 0; i < n; i++)
ecc[i] = std::max(dp[i], dq[i]);
return {dp[q], p, q, ecc};
}
/// @brief Weighted tree diameter and one realizing vertex path.
/// @return (distance, path from one endpoint to the other).
template <class Weight>
std::pair<Weight, std::vector<int>> weighted_tree_diameter(
const std::vector<std::vector<std::pair<int, Weight>>> &G) {
if (G.empty()) {
return {Weight{}, {}};
}
auto dfs = [&](int s) {
std::vector<Weight> dis(G.size());
std::vector<int> fa(G.size(), -1);
std::vector<int> stk = {s};
fa[s] = s;
for (int idx = 0; idx < int(stk.size()); idx++) {
int u = stk[idx];
for (auto [nxt, w] : G[u]) {
if (fa[nxt] != -1) {
continue;
}
fa[nxt] = u;
dis[nxt] = dis[u] + w;
stk.push_back(nxt);
}
}
int far = s;
for (int u = 0; u < int(G.size()); u++) {
if (dis[far] < dis[u]) {
far = u;
}
}
return std::tuple{far, std::move(dis), std::move(fa)};
};
auto [a1, id1, ip] = dfs(0);
auto [b1, dis, fa] = dfs(a1);
std::vector<int> pth;
for (int u = b1;; u = fa[u]) {
pth.push_back(u);
if (u == a1) {
break;
}
}
std::reverse(pth.begin(), pth.end());
return {dis[b1], pth};
}
/// @brief Diameter monoid for segment tree. Merge two vertex sets and track the farthest pair.
struct diameter_monoid {
using value_type = std::pair<int64_t, std::array<int, 2>>;
using S = value_type;
using X = value_type;
static constexpr value_type identity = {-1, {-1, -1}};
static constexpr bool commute = true;
diameter_monoid() = default;
explicit diameter_monoid(const fastlca &l) { set_lca(l); }
static const fastlca *&get_lca() {
static const fastlca *lca = nullptr;
return lca;
}
static void set_lca(const fastlca &l) { get_lca() = &l; }
static value_type unit() { return identity; }
static value_type e() { return unit(); }
static value_type make(int v) { return {0, {v, v}}; }
static value_type from_vertex(int v) { return make(v); }
static value_type op(value_type a, value_type b) {
if (a == unit()) return b;
if (b == unit()) return a;
const fastlca *lca = get_lca();
assert(lca != nullptr);
value_type c = std::max(a, b);
for (auto x : a.second)
for (auto y : b.second) {
int64_t d = lca->distance(x, y);
if (d > c.first)
c = {d, {x, y}};
}
return c;
}
static value_type merge(value_type a, value_type b) { return op(a, b); }
};
} // namespace noya
#endif // NOYA_TREE_DIAMETER_HPP
/// @complexity Time: O(n) for a tree diameter; O(1) diameter-monoid merge.
/// Space: O(n).
/// @complexity Time: O(n log n) build, O(1) LCA/distance/ancestor checks.
/// Space: O(n log n). Path listing is linear in the returned path length.
/// @complexity Time: O(n log n) build and O(1) idempotent range query.
/// Space: O(n log n).
#include <bit>
#include <cassert>
#include <utility>
#include <vector>
namespace noya {
/// @brief Static O(1) range product for an idempotent semilattice.
/// Monoid provides value_type, unit(), and an associative, idempotent op().
/// Empty ranges return unit(); input arrays are owned by the table.
/// @details For k = floor(log2(r-l)), [l, r) is covered by the length-2^k
/// blocks beginning at l and ending at r. They may overlap, so combining them
/// is correct precisely because op(x, x) = x.
template <class Monoid> class sparse_table {
public:
using value_type = typename Monoid::value_type;
sparse_table() = default;
explicit sparse_table(std::vector<value_type> a) { build(std::move(a)); }
template <class F> sparse_table(int n_, F f) { build(n_, std::move(f)); }
template <class F> void build(int n_, F f) {
assert(n_ >= 0);
std::vector<value_type> a;
a.reserve(n_);
for (int i = 0; i < n_; i++) {
a.push_back(f(i));
}
build(std::move(a));
}
/// @brief Rebuild from a static array.
void build(std::vector<value_type> a) {
n = int(a.size());
int lg = int(std::bit_width(static_cast<unsigned>(n)));
st.resize(lg);
if (n == 0) {
return;
}
st[0] = std::move(a);
for (int dep = 1; dep < lg; dep++) {
int w = 1 << dep;
int hf = w >> 1;
st[dep].resize(n - w + 1, Monoid::unit());
for (int l = 0; l + w <= n; l++) {
st[dep][l] = Monoid::op(st[dep - 1][l], st[dep - 1][l + hf]);
}
}
}
int size() const { return n; }
bool empty() const { return n == 0; }
value_type get(int p) const {
assert(0 <= p && p < n);
return st[0][p];
}
value_type all_prod() const { return prod(0, n); }
/// @brief Return the idempotent product over [l, r).
value_type prod(int l, int r) const {
assert(0 <= l && l <= r && r <= n);
if (l == r) {
return Monoid::unit();
}
int dep = int(std::bit_width(static_cast<unsigned>(r - l))) - 1;
int w = 1 << dep;
return Monoid::op(st[dep][l], st[dep][r - w]);
}
private:
int n = 0;
std::vector<std::vector<value_type>> st;
};
} // namespace noya
/// @complexity Time: O(n) build; common queries add O(1) work to their LCA calls,
/// except path(), which takes O(path length). Space: O(n).
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
namespace noya {
/// @brief Iterative rooted-tree preprocessing shared by RMQ LCA and HLD.
/// The graph is a connected tree, with integer endpoints or (endpoint, weight)
/// pairs. Root depth is zero, root parent is -1, and subtree ranges are [l, r).
/// No adjacency-list copy, RMQ table, jump table, or heavy paths are retained.
struct tree_preprocessing {
int n = 0;
int rt = -1;
std::vector<int> fa, d, dfn, siz, tl;
tree_preprocessing() = default;
template <class Edge>
explicit tree_preprocessing(const std::vector<std::vector<Edge>> &g,
int root = 0) {
build(g, root);
}
template <class Edge> static int endpoint(const Edge &edge) {
if constexpr (std::is_integral_v<Edge>) {
return int(edge);
} else {
return int(std::get<0>(edge));
}
}
template <class Edge>
void build(const std::vector<std::vector<Edge>> &g, int root = 0) {
n = int(g.size());
rt = n == 0 ? -1 : root;
fa.assign(n, -1);
d.assign(n, 0);
dfn.assign(n, -1);
siz.assign(n, 1);
tl.clear();
tl.reserve(n);
if (n == 0) {
return;
}
assert(0 <= root && root < n);
std::vector<int> stack{root};
while (!stack.empty()) {
int u = stack.back();
stack.pop_back();
assert(dfn[u] == -1);
dfn[u] = int(tl.size());
tl.push_back(u);
for (auto it = g[u].rbegin(); it != g[u].rend(); ++it) {
int v = endpoint(*it);
assert(0 <= v && v < n);
if (v != fa[u]) {
fa[v] = u;
d[v] = d[u] + 1;
stack.push_back(v);
}
}
}
assert(int(tl.size()) == n);
for (auto it = tl.rbegin(); it != tl.rend(); ++it) {
if (fa[*it] != -1) {
siz[fa[*it]] += siz[*it];
}
}
}
void build(const std::vector<std::vector<int>> &g = {}, int root = 0) {
build<int>(g, root);
}
bool is_subtree(int a, int b) const {
assert(0 <= a && a < n && 0 <= b && b < n);
return dfn[b] <= dfn[a] && dfn[a] < dfn[b] + siz[b];
}
std::array<int, 2> subtree(int u) const {
assert(0 <= u && u < n);
return {dfn[u], dfn[u] + siz[u]};
}
};
/// @brief Shared tree queries with compile-time dispatch to Derived::lca().
template <class Derived> struct tree_queries : tree_preprocessing {
int rooted_lca(int a, int b, int root) const {
const auto &self = static_cast<const Derived &>(*this);
return self.lca(a, b) ^ self.lca(a, root) ^ self.lca(b, root);
}
std::int64_t distance(int a, int b) const {
const auto &self = static_cast<const Derived &>(*this);
int c = self.lca(a, b);
return std::int64_t(d[a]) + d[b] - 2 * std::int64_t(d[c]);
}
std::pair<int, int> intersection(int a, int b, int c, int e) const {
const auto &self = static_cast<const Derived &>(*this);
int ab = self.lca(a, b), ac = self.lca(a, c), ae = self.lca(a, e);
int bc = self.lca(b, c), be = self.lca(b, e), ce = self.lca(c, e);
int x = ab ^ ac ^ bc;
int y = ab ^ ae ^ be;
if (x != y) {
return {x, y};
}
int z = ac ^ ae ^ ce;
return x == z ? std::pair{x, x} : std::pair{-1, -1};
}
std::pair<int, int> intersection(std::pair<int, int> a,
std::pair<int, int> b) const {
return intersection(a.first, a.second, b.first, b.second);
}
std::vector<int> path(int a, int b) const {
const auto &self = static_cast<const Derived &>(*this);
int c = self.lca(a, b);
std::vector<int> left, right;
while (a != c) {
left.push_back(a);
a = fa[a];
}
while (b != c) {
right.push_back(b);
b = fa[b];
}
left.push_back(c);
left.insert(left.end(), right.rbegin(), right.rend());
return left;
}
};
} // namespace noya
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#include <vector>
namespace noya {
/// @brief RMQ LCA with O(1) queries, without heavy paths or a binary jump table.
/// RMQ stores only parent DFS indices; tl maps the minimum back to a vertex.
/// Weighted adjacency automatically enables weighted distance; rebuilding from
/// unweighted adjacency resets it. Use hld for kth_ancestor/kth_on_path queries.
/// Weights must be integers (including signed/unsigned __int128). Each weight,
/// root distance, and returned distance must be representable as int64_t.
struct fastlca : tree_queries<fastlca> {
struct minimum_index_semilattice {
using value_type = int;
static value_type unit() { return std::numeric_limits<int>::max(); }
static value_type op(value_type a, value_type b) { return std::min(a, b); }
};
sparse_table<minimum_index_semilattice> ST;
std::vector<std::int64_t> len;
bool wt = false;
fastlca() = default;
template <class Edge>
explicit fastlca(const std::vector<std::vector<Edge>> &g, int root = 0) {
build(g, root);
}
template <class Edge>
void build(const std::vector<std::vector<Edge>> &g, int root = 0) {
tree_preprocessing::build(g, root);
std::vector<int> rmq(n);
for (int u : tl) {
rmq[dfn[u]] = fa[u] == -1 ? -1 : dfn[fa[u]];
}
ST.build(std::move(rmq));
wt = !std::is_integral_v<Edge>;
len.clear();
if constexpr (!std::is_integral_v<Edge>) {
using Weight = std::remove_cvref_t<decltype(std::get<1>(std::declval<Edge>()))>;
static_assert(std::numeric_limits<Weight>::is_integer,
"fastlca requires integer edge weights");
static_assert(sizeof(Weight) <= sizeof(__int128),
"fastlca supports integer weights up to 128 bits");
len.assign(n, 0);
for (int u : tl) {
for (const auto &edge : g[u]) {
int v = endpoint(edge);
if (fa[v] == u) {
const auto weight = std::get<1>(edge);
if constexpr (std::numeric_limits<Weight>::is_signed) {
assert(__int128(weight) >= std::numeric_limits<std::int64_t>::lowest() &&
__int128(weight) <= std::numeric_limits<std::int64_t>::max());
} else {
assert(__uint128_t(weight) <=
__uint128_t(std::numeric_limits<std::int64_t>::max()));
}
__int128 value = __int128(len[u]) + __int128(weight);
assert(std::numeric_limits<std::int64_t>::lowest() <= value &&
value <= std::numeric_limits<std::int64_t>::max());
len[v] = std::int64_t(value);
}
}
}
}
}
void build(const std::vector<std::vector<int>> &g = {}, int root = 0) {
build<int>(g, root);
}
int lca(int u, int v) const {
assert(0 <= u && u < n && 0 <= v && v < n);
if (u == v) {
return u;
}
int a = dfn[u], b = dfn[v];
if (a > b) {
std::swap(a, b);
}
return tl[ST.prod(a + 1, b + 1)];
}
std::int64_t distance(int a, int b) const {
if (!wt) {
return tree_queries<fastlca>::distance(a, b);
}
int c = lca(a, b);
__int128 value = __int128(len[a]) + len[b] - 2 * __int128(len[c]);
assert(std::numeric_limits<std::int64_t>::lowest() <= value &&
value <= std::numeric_limits<std::int64_t>::max());
return std::int64_t(value);
}
};
} // namespace noya
/// @complexity Time: O(V + E) BFS/0-1 BFS, O((V + E) log V) Dijkstra, O(VE) signed-cycle routines.
/// Space: O(V + E).
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <deque>
#include <functional>
#include <limits>
#include <queue>
#include <tuple>
#include <utility>
#include <vector>
namespace noya {
/// @brief BFS on unweighted graph. @return (distances, predecessors).
inline std::pair<std::vector<int>, std::vector<int>>
bfs_unweighted(std::vector<std::vector<int>> &g, int src) {
int N = int(g.size());
const int INF = std::numeric_limits<int>::max();
std::vector<int> dis(N, INF);
std::vector<int> pre(N, -1);
dis[src] = 0;
pre[src] = src;
std::vector<int> que{src};
for (int i = 0; i < int(que.size()); i++) {
int u = que[i];
for (auto v : g[u])
if (dis[v] == INF) {
dis[v] = dis[u] + 1;
pre[v] = u;
que.push_back(v);
}
}
return {dis, pre};
}
/// @brief Dijkstra for nonnegative integer weights. Unreachable/unrepresentable
/// distances use int64_t::max(). @return (distances, predecessors).
template <class T>
std::pair<std::vector<int64_t>, std::vector<int>>
dijkstra(std::vector<std::vector<T>> &g, int src) {
int N = int(g.size());
const int64_t INF = std::numeric_limits<int64_t>::max();
std::vector<int64_t> dis(N, INF);
std::vector<int> pre(N, -1);
std::priority_queue<std::pair<int64_t, int>,
std::vector<std::pair<int64_t, int>>, std::greater<>>
que;
que.emplace(dis[src] = 0, src);
pre[src] = src;
while (!que.empty()) {
auto [d, u] = que.top();
que.pop();
if (d > dis[u])
continue;
for (auto &[v, w] : g[u]) {
assert(w >= 0);
__int128 can = __int128(dis[u]) + __int128(w);
if (can < dis[v]) {
dis[v] = static_cast<int64_t>(can);
pre[v] = u;
que.emplace(dis[v], v);
}
}
}
return {dis, pre};
}
/// @brief Shortest paths in a graph whose edge weights are 0 or 1.
/// @return (distances, predecessors).
template <class T>
std::pair<std::vector<int64_t>, std::vector<int>>
bfs01(const std::vector<std::vector<T>> &g, int src) {
const int N = int(g.size());
const int64_t INF = std::numeric_limits<int64_t>::max();
std::vector<int64_t> dis(N, INF);
std::vector<int> pre(N, -1);
std::deque<int> que;
dis[src] = 0;
pre[src] = src;
que.push_front(src);
while (!que.empty()) {
int u = que.front();
que.pop_front();
for (const auto &[v, w1] : g[u]) {
assert(w1 == 0 || w1 == 1);
if (dis[v] <= dis[u] + w1) {
continue;
}
dis[v] = dis[u] + w1;
pre[v] = u;
if (w1 == 0) {
que.push_front(v);
} else {
que.push_back(v);
}
}
}
return {dis, pre};
}
struct bellman_ford_result {
std::vector<int64_t> di1;
std::vector<int> prv;
std::vector<bool> neg;
};
/// @brief Return edge ids forming one negative directed cycle anywhere in the
/// graph, in traversal order, or an empty vector when no negative cycle exists.
template <class Weight>
std::vector<int>
find_negative_cycle(int n,
const std::vector<std::tuple<int, int, Weight>> &es) {
assert(n >= 0);
std::vector<__int128> di1(n);
std::vector<int> pe(n, -1);
int cv = -1;
for (int ite = 0; ite < n; ite++) {
cv = -1;
for (int id = 0; id < int(es.size()); id++) {
auto [u1, to, w1] = es[id];
assert(0 <= u1 && u1 < n);
assert(0 <= to && to < n);
__int128 can = di1[u1] + __int128(w1);
if (can < di1[to]) {
di1[to] = can;
pe[to] = id;
cv = to;
}
}
}
if (cv == -1) {
return {};
}
for (int stp = 0; stp < n; stp++) {
int id = pe[cv];
assert(id != -1);
cv = std::get<0>(es[id]);
}
int src = cv;
std::vector<int> cyc;
do {
int id = pe[cv];
assert(id != -1);
cyc.push_back(id);
cv = std::get<0>(es[id]);
} while (cv != src);
std::reverse(cyc.begin(), cyc.end());
return cyc;
}
/// @brief Bellman-Ford with propagation from source-reachable negative cycles.
/// A negative-infinite vertex has distance numeric_limits<int64_t>::lowest().
template <class T>
bellman_ford_result bellman_ford(const std::vector<std::vector<T>> &g,
int src) {
const int N = int(g.size());
const int64_t INF = std::numeric_limits<int64_t>::max();
const int64_t NIF = std::numeric_limits<int64_t>::lowest();
const __int128 WIF = __int128(1) << 120;
std::vector<__int128> wd(N, WIF);
std::vector<int> pre(N, -1);
std::vector<bool> neg(N);
wd[src] = 0;
pre[src] = src;
for (int ite = 0; ite < N; ite++) {
bool upd = false;
for (int u = 0; u < N; u++) {
if (wd[u] == WIF) {
continue;
}
for (const auto &[v, w1] : g[u]) {
__int128 can = wd[u] + static_cast<__int128>(w1);
if (can >= wd[v]) {
continue;
}
wd[v] = can;
pre[v] = u;
upd = true;
if (ite == N - 1) {
neg[v] = true;
}
}
}
if (!upd) {
break;
}
}
std::vector<int> que;
for (int u = 0; u < N; u++) {
if (neg[u]) {
que.push_back(u);
}
}
for (int i = 0; i < int(que.size()); i++) {
int u = que[i];
for (const auto &[v, w1] : g[u]) {
(void)w1;
if (!neg[v]) {
neg[v] = true;
que.push_back(v);
}
}
}
std::vector<int64_t> dis(N, INF);
for (int u = 0; u < N; u++) {
if (neg[u] || wd[u] < __int128(NIF)) {
dis[u] = NIF;
} else if (wd[u] < WIF) {
dis[u] = wd[u] > __int128(INF)
? INF
: static_cast<int64_t>(wd[u]);
}
}
return {dis, pre, neg};
}
inline std::vector<int> find_path(std::vector<int> &pre, int s, int t) {
std::vector<int> pth;
int cur = t;
while (cur != s) {
assert(cur >= 0);
pth.push_back(cur);
cur = pre[cur];
}
pth.push_back(s);
std::reverse(pth.begin(), pth.end());
return pth;
}
/// @brief All-pairs integer distances; numeric_limits<T>::max()/2 denotes
/// an absent edge and unreachable pairs. Finite distances must lie below that
/// sentinel and fit int64_t; the graph must have no negative cycle.
template <class T>
std::vector<std::vector<int64_t>> floyd(std::vector<std::vector<T>> &g) {
int N = int(g.size());
const int64_t INF = std::numeric_limits<T>::max() / 2;
std::vector<std::vector<int64_t>> f(N, std::vector<int64_t>(N, INF));
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
f[i][j] = g[i][j];
for (int i = 0; i < N; i++)
f[i][i] = std::min<int64_t>(f[i][i], 0);
for (int k = 0; k < N; k++)
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
if (f[i][k] != INF && f[k][j] != INF) {
__int128 can = __int128(f[i][k]) + f[k][j];
assert(can >= std::numeric_limits<int64_t>::lowest());
if (can < f[i][j]) {
f[i][j] = static_cast<int64_t>(can);
}
}
return f;
}
} // namespace noya
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <tuple>
#include <utility>
#include <vector>
namespace noya {
/// @brief Unweighted tree diameter via double BFS. @return (diameter, u, v, eccentricity[]).
inline std::tuple<int, int, int, std::vector<int>>
tree_diam(std::vector<std::vector<int>> &g) {
if (g.empty())
return {-1, -1, -1, {}};
auto d0 = bfs_unweighted(g, 0).first;
int p = std::max_element(d0.begin(), d0.end()) - d0.begin();
auto dp = bfs_unweighted(g, p).first;
int q = std::max_element(dp.begin(), dp.end()) - dp.begin();
auto dq = bfs_unweighted(g, q).first;
int n = int(g.size());
std::vector<int> ecc(n);
for (int i = 0; i < n; i++)
ecc[i] = std::max(dp[i], dq[i]);
return {dp[q], p, q, ecc};
}
/// @brief Weighted tree diameter and one realizing vertex path.
/// @return (distance, path from one endpoint to the other).
template <class Weight>
std::pair<Weight, std::vector<int>> weighted_tree_diameter(
const std::vector<std::vector<std::pair<int, Weight>>> &G) {
if (G.empty()) {
return {Weight{}, {}};
}
auto dfs = [&](int s) {
std::vector<Weight> dis(G.size());
std::vector<int> fa(G.size(), -1);
std::vector<int> stk = {s};
fa[s] = s;
for (int idx = 0; idx < int(stk.size()); idx++) {
int u = stk[idx];
for (auto [nxt, w] : G[u]) {
if (fa[nxt] != -1) {
continue;
}
fa[nxt] = u;
dis[nxt] = dis[u] + w;
stk.push_back(nxt);
}
}
int far = s;
for (int u = 0; u < int(G.size()); u++) {
if (dis[far] < dis[u]) {
far = u;
}
}
return std::tuple{far, std::move(dis), std::move(fa)};
};
auto [a1, id1, ip] = dfs(0);
auto [b1, dis, fa] = dfs(a1);
std::vector<int> pth;
for (int u = b1;; u = fa[u]) {
pth.push_back(u);
if (u == a1) {
break;
}
}
std::reverse(pth.begin(), pth.end());
return {dis[b1], pth};
}
/// @brief Diameter monoid for segment tree. Merge two vertex sets and track the farthest pair.
struct diameter_monoid {
using value_type = std::pair<int64_t, std::array<int, 2>>;
using S = value_type;
using X = value_type;
static constexpr value_type identity = {-1, {-1, -1}};
static constexpr bool commute = true;
diameter_monoid() = default;
explicit diameter_monoid(const fastlca &l) { set_lca(l); }
static const fastlca *&get_lca() {
static const fastlca *lca = nullptr;
return lca;
}
static void set_lca(const fastlca &l) { get_lca() = &l; }
static value_type unit() { return identity; }
static value_type e() { return unit(); }
static value_type make(int v) { return {0, {v, v}}; }
static value_type from_vertex(int v) { return make(v); }
static value_type op(value_type a, value_type b) {
if (a == unit()) return b;
if (b == unit()) return a;
const fastlca *lca = get_lca();
assert(lca != nullptr);
value_type c = std::max(a, b);
for (auto x : a.second)
for (auto y : b.second) {
int64_t d = lca->distance(x, y);
if (d > c.first)
c = {d, {x, y}};
}
return c;
}
static value_type merge(value_type a, value_type b) { return op(a, b); }
};
} // namespace noya
// NOYA_TREE_DIAMETER_HPP