競技プログラミング用 C++ ライブラリ
tree/lca.hpp · GitHub で見る
共通ページ
根付き木の 2 頂点に対し、両方の祖先で最も深い頂点を求めます。add_edge で木の辺を全て追加し、build を呼んでから問い合わせます。
#include "lib/tree/lca.hpp"
LCA tree(3);
tree.add_edge(0, 1);
tree.add_edge(1, 2);
tree.build();
int v = tree.lca(0, 2); // 0
int distance = tree.dist(0, 2); // 2
auto [l, r] = tree.subtree(1);
int count = r - l; // 2
| 操作 | 内容・返り値 | 計算量 |
|---|---|---|
LCA(n = 0) |
頂点数を指定 | \(O(n)\) |
add_edge(u,v) |
無向辺を追加し、辺 ID を返す | 償却 \(O(1)\) |
build(root = 0) |
指定した根で前処理 | \(O(n\log n)\) |
build_from_parents(parents) |
親配列から木を構築して前処理 | \(O(n\log n)\) |
lca(u,v) |
最小共通祖先 | \(O(1)\) |
dist(u,v) |
経路の辺数 | \(O(1)\) |
is_ancestor(u,v) |
u が v の祖先か。自身も含む |
\(O(1)\) |
subtree(v) |
DFS の行きがけ順での部分木の半開区間 [l,r)。l は v の位置 |
\(O(1)\) |
parent[v] / depth[v]
|
親(根は -1)/根からの辺数 | \(O(1)\) |
n / edges[id]
|
全頂点数/辺の両端 | \(O(1)\) |
無向木を扱い、0 <= n <= INT_MAX/2、頂点番号は [0,n) とします。build(root = 0) で根を指定します。空の木では頂点を指定する問い合わせはできません。公開の n・辺・親・深さは直接書き換えないでください。
親配列がある場合は build_from_parents(std::vector<int> parents) を使えます。頂点数は配列の長さとなり、根の親を -1、それ以外の親を [0,n) の頂点番号で指定します。根はちょうど 1 頂点で、全頂点が根につながる木である必要があります。頂点番号は任意の順序で構いません。空配列も渡せます。
#include "lib/tree/lca.hpp"
LCA rooted;
rooted.build_from_parents({-1, 0, 1});
int ancestor = rooted.lca(1, 2); // 1
既存の頂点・辺は置き換わります。edges には根以外の頂点 v の (parents[v], v) が v の昇順で入り、その順番が辺 ID になります。配列を以降使わない場合は build_from_parents(std::move(parents)) と渡せます。
subtree(v) の区間は、元の頂点番号の区間ではありません。根から DFS して初めて訪れる順に各頂点へ番号を付けたとき、v とその子孫が占める区間 [l,r) です。l が v 自身の DFS 番号、r-l が v を含む部分木の頂点数です。
辺を追加する段階で閉路のない連結な木にし、空でなければ辺数を n-1 にしてください。辺や根を変えた後は build を呼び直します。距離は重みの合計ではなく辺数です。
前処理・空間計算量は \(O(n \log n)\)、各クエリは \(O(1)\) です。
#ifndef LCA_HPP
#define LCA_HPP
#include "../components/static_range_min.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <limits>
#include <utility>
#include <vector>
class LCA {
std::vector<int> in, out, order;
bool ordered_ = false;
StaticRangeMin<int, true> table;
void check([[maybe_unused]] int v) const { assert(0 <= v && v < n && static_cast<int>(in.size()) == n); }
void finish(int root) {
in[root] = n;
ordered_ = root == 0;
for (int i = n - 2; i >= 0; --i) {
int v = order[i], p = parent[v];
depth[v] = depth[p] + 1;
in[v] = in[p];
in[p] -= out[v];
}
for (int v = 0; v < n; ++v) {
--in[v];
out[v] += in[v];
order[in[v]] = v;
ordered_ &= parent[v] < v;
}
std::vector<int> values(n);
for (int i = 1; i < n; ++i) values[i] = ordered_ ? parent[order[i]] : in[parent[order[i]]];
table.build(std::move(values));
parent[root] = -1;
}
public:
int n;
std::vector<std::pair<int, int>> edges;
std::vector<int> parent, depth;
explicit LCA(int n = 0) : n(n) {
assert(0 <= n && n <= std::numeric_limits<int>::max() / 2);
edges.reserve(std::max(0, n - 1));
}
int add_edge(int u, int v) {
assert(0 <= u && u < n && 0 <= v && v < n);
edges.emplace_back(u, v);
return static_cast<int>(edges.size()) - 1;
}
void build(int root = 0) {
assert(n >= 0 && edges.size() == std::size_t(std::max(0, n - 1)));
assert(!n || (0 <= root && root < n));
in.resize(n);
out.resize(n);
order.resize(n);
parent.assign(n, -1);
depth.assign(n, 0);
table = StaticRangeMin<int, true>();
if (!n) return;
std::vector<int> degree(n), neighbors(n);
for (auto [u, v] : edges) {
++degree[u], ++degree[v];
neighbors[u] ^= v, neighbors[v] ^= u;
}
degree[root] += 2;
std::fill(out.begin(), out.end(), 1);
int count = 0;
for (int v = 0; v < n; ++v) if (degree[v] == 1) order[count++] = v;
for (int i = 0; i < count; ++i) {
int v = order[i], p = neighbors[v];
parent[v] = p;
out[p] += out[v];
neighbors[p] ^= v;
if (--degree[p] == 1) order[count++] = p;
}
assert(count == n - 1);
finish(root);
}
void build_from_parents(std::vector<int> parents) {
assert(parents.size() <= std::size_t(std::numeric_limits<int>::max() / 2));
n = static_cast<int>(parents.size());
parent = std::move(parents);
in.resize(n);
out.assign(n, 1);
order.resize(n);
depth.assign(n, 0);
table = StaticRangeMin<int, true>();
edges.clear();
edges.reserve(std::max(0, n - 1));
if (!n) return;
int root = -1;
bool ordered = true;
for (int v = 0; v < n; ++v) {
int p = parent[v];
assert(-1 <= p && p < n && p != v);
if (p == -1) {
assert(root == -1);
root = v;
} else {
edges.emplace_back(p, v);
ordered &= p < v;
}
}
assert(root != -1);
if (ordered) {
for (int v = n - 1; v > 0; --v) {
out[parent[v]] += out[v];
order[n - 1 - v] = v;
}
} else {
std::fill(in.begin(), in.end(), 0);
for (int v = 0; v < n; ++v) if (v != root) ++in[parent[v]];
int count = 0;
for (int v = 0; v < n; ++v) if (!in[v] && v != root) order[count++] = v;
for (int i = 0; i < count; ++i) {
int v = order[i], p = parent[v];
out[p] += out[v];
if (!--in[p] && p != root) order[count++] = p;
}
assert(count == n - 1);
}
finish(root);
}
int lca(int u, int v) const {
check(u);
check(v);
int l = in[u], r = in[v];
if (l == r) return u;
if (l > r) std::swap(l, r);
++l;
int w = table.prod(l, r + 1);
return ordered_ ? w : order[w];
}
int dist(int u, int v) const {
int w = lca(u, v);
return depth[u] + depth[v] - 2 * depth[w];
}
bool is_ancestor(int u, int v) const {
check(u);
check(v);
return in[u] <= in[v] && in[v] < out[u];
}
std::pair<int, int> subtree(int v) const {
check(v);
return {in[v], out[v]};
}
};
#endif
#line 1 "tree/lca.hpp"
#line 1 "components/static_range_min.hpp"
#line 1 "components/default_init_allocator.hpp"
#include <memory>
#include <new>
template <class T>
struct DefaultInitAllocator : std::allocator<T> {
template <class U>
struct rebind {
using other = DefaultInitAllocator<U>;
};
DefaultInitAllocator() = default;
template <class U>
DefaultInitAllocator(const DefaultInitAllocator<U> &) noexcept {}
template <class U>
void construct(U *p) { ::new (static_cast<void *>(p)) U; }
};
#line 1 "data_structure/sparse_table.hpp"
#line 5 "data_structure/sparse_table.hpp"
#include <algorithm>
#include <bit>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#include <vector>
template <class S, auto op, auto e = nullptr>
class SparseTable {
using Value = std::conditional_t<std::is_same_v<S, bool>, std::uint8_t, S>;
using Reference = std::conditional_t<std::is_same_v<S, bool>, bool, const S &>;
int n = 0;
std::vector<std::vector<Value, DefaultInitAllocator<Value>>> data;
static decltype(auto) combine(const Value &a, const Value &b) {
if constexpr (std::is_same_v<S, bool> || !std::is_invocable_v<decltype(op), const S &, const S &>) return S(op(S(a), S(b)));
else return op(a, b);
}
public:
SparseTable() = default;
SparseTable(const SparseTable &) = default;
SparseTable &operator=(const SparseTable &) = default;
SparseTable(SparseTable &&other) noexcept : n(std::exchange(other.n, 0)), data(std::move(other.data)) {}
SparseTable &operator=(SparseTable &&other) noexcept {
if (this != &other) {
n = std::exchange(other.n, 0);
data = std::move(other.data);
}
return *this;
}
explicit SparseTable(const std::vector<S> &a) { build(a); }
void clear() {
data.clear();
n = 0;
}
void build(const std::vector<S> &a) {
assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
build(static_cast<int>(a.size()), [&](int i) -> S { return a[i]; });
}
template <class F>
void build(int size, F value) {
assert(size >= 0);
n = size;
data.resize(std::bit_width(unsigned(n)));
if (!n) return;
if constexpr (std::is_default_constructible_v<Value>) {
data[0].resize(n);
for (int i = 0; i < n; ++i) data[0][i] = value(i);
} else {
data[0].clear();
data[0].reserve(n);
for (int i = 0; i < n; ++i) data[0].push_back(value(i));
}
for (int k = 1; k < std::bit_width(unsigned(n)); ++k) {
int length = n - (1U << k) + 1, half = 1U << (k - 1);
if constexpr (std::is_default_constructible_v<Value>) {
data[k].resize(length);
for (int i = 0; i < length; ++i) data[k][i] = combine(data[k - 1][i], data[k - 1][i + half]);
} else {
data[k].clear();
data[k].reserve(length);
for (int i = 0; i < length; ++i) data[k].push_back(combine(data[k - 1][i], data[k - 1][i + half]));
}
}
}
int size() const { return n; }
bool empty() const { return n == 0; }
Reference operator[](int i) const {
assert(0 <= i && i < n);
return data[0][i];
}
[[gnu::always_inline]] S prod(int l, int r) const {
assert(0 <= l && l <= r && r <= n);
if constexpr (std::is_same_v<decltype(e), std::nullptr_t>) assert(l < r);
else if (l == r) return e();
return prod_unchecked(l, r);
}
[[gnu::always_inline]] S prod_unchecked(int l, int r) const {
int k = std::bit_width(unsigned(r - l)) - 1;
return combine(data[k][l], data[k][r - (1U << k)]);
}
S all_prod() const { return prod(0, n); }
};
#line 7 "components/static_range_min.hpp"
#include <array>
#line 12 "components/static_range_min.hpp"
#include <span>
#line 16 "components/static_range_min.hpp"
template <class T = int, bool FastBoundaries = false> class StaticRangeMin {
static T minimum(T a, T b) { return b < a ? b : a; }
std::vector<T> values_;
std::vector<std::uint32_t, DefaultInitAllocator<std::uint32_t>> masks_;
SparseTable<T, minimum> blocks_;
struct Boundaries { std::vector<T> prefix, suffix; };
struct Empty {};
[[no_unique_address]] std::conditional_t<FastBoundaries, Boundaries, Empty> boundaries_;
T within(int l, int r) const {
auto mask = masks_[r - 1] & (~std::uint32_t(0) << (l & 31));
return values_[(l & ~31) + std::countr_zero(mask)];
}
public:
StaticRangeMin() = default;
explicit StaticRangeMin(std::vector<T> values) { build(std::move(values)); }
void build(std::vector<T> values) {
assert(values.size() <= std::size_t(std::numeric_limits<int>::max()));
values_ = std::move(values);
int n = size();
masks_.resize(n);
for (int first = 0; first < n;) {
int last = first + std::min(32, n - first);
std::uint32_t mask = 0;
for (int i = first; i < last; ++i) {
while (mask && !(values_[first + std::bit_width(mask) - 1] < values_[i]))
mask &= ~(std::uint32_t(1) << (std::bit_width(mask) - 1));
mask |= std::uint32_t(1) << (i - first);
masks_[i] = mask;
}
first = last;
}
if constexpr (FastBoundaries) {
boundaries_.prefix = values_, boundaries_.suffix = values_;
for (int first = 0; first < n;) {
int last = first + std::min(32, n - first);
for (int i = first + 1; i < last; ++i) boundaries_.prefix[i] = minimum(boundaries_.prefix[i - 1], values_[i]);
for (int i = last - 1; i > first; --i) boundaries_.suffix[i - 1] = minimum(values_[i - 1], boundaries_.suffix[i]);
first = last;
}
}
blocks_.build(n ? (n - 1) / 32 + 1 : 0, [&](int i) {
int last = i * 32 + std::min(32, n - i * 32);
if constexpr (FastBoundaries) return T(boundaries_.prefix[last - 1]);
else return within(i * 32, last);
});
}
int size() const { return static_cast<int>(values_.size()); }
bool empty() const { return values_.empty(); }
std::conditional_t<std::is_same_v<T, bool>, bool, const T &> operator[](int i) const {
assert(0 <= i && i < size());
return values_[i];
}
T prod(int l, int r) const {
assert(0 <= l && l < r && r <= size());
int a = l / 32, b = (r - 1) / 32;
if (a == b) return within(l, r);
T value = [&] {
if constexpr (FastBoundaries) return minimum(boundaries_.suffix[l], boundaries_.prefix[r - 1]);
else return minimum(within(l, (a + 1) * 32), within(b * 32, r));
}();
if (a + 1 < b) value = minimum(value, blocks_.prod_unchecked(a + 1, b));
return value;
}
std::vector<T> prod_many(std::span<const std::pair<int, int>> ranges) const {
std::vector<T> result;
result.reserve(ranges.size());
if constexpr (FastBoundaries) {
for (auto [l, r] : ranges) result.emplace_back(prod(l, r));
} else {
std::array<int, 32> left, right;
for (std::size_t first = 0; first < ranges.size(); first += 32) {
int count = int(std::min<std::size_t>(32, ranges.size() - first));
for (int i = 0; i < count; ++i) {
auto [l, r] = ranges[first + i];
assert(0 <= l && l < r && r <= size());
int a = l & ~31, b = (r - 1) & ~31;
auto mask = masks_[a == b ? r - 1 : a + 31] & (~std::uint32_t(0) << (l & 31));
left[i] = a + std::countr_zero(mask);
right[i] = a == b ? left[i] : b + std::countr_zero(masks_[r - 1]);
#if defined(__GNUC__) || defined(__clang__)
if constexpr (!std::is_same_v<T, bool>) {
__builtin_prefetch(values_.data() + left[i]);
__builtin_prefetch(values_.data() + right[i]);
}
#endif
}
for (int i = 0; i < count; ++i) {
auto [l, r] = ranges[first + i];
T value = minimum(values_[left[i]], values_[right[i]]);
int a = l / 32 + 1, b = (r - 1) / 32;
if (a < b) value = minimum(value, blocks_.prod_unchecked(a, b));
result.emplace_back(std::move(value));
}
}
}
return result;
}
T all_prod() const { return prod(0, size()); }
};
#line 11 "tree/lca.hpp"
class LCA {
std::vector<int> in, out, order;
bool ordered_ = false;
StaticRangeMin<int, true> table;
void check([[maybe_unused]] int v) const { assert(0 <= v && v < n && static_cast<int>(in.size()) == n); }
void finish(int root) {
in[root] = n;
ordered_ = root == 0;
for (int i = n - 2; i >= 0; --i) {
int v = order[i], p = parent[v];
depth[v] = depth[p] + 1;
in[v] = in[p];
in[p] -= out[v];
}
for (int v = 0; v < n; ++v) {
--in[v];
out[v] += in[v];
order[in[v]] = v;
ordered_ &= parent[v] < v;
}
std::vector<int> values(n);
for (int i = 1; i < n; ++i) values[i] = ordered_ ? parent[order[i]] : in[parent[order[i]]];
table.build(std::move(values));
parent[root] = -1;
}
public:
int n;
std::vector<std::pair<int, int>> edges;
std::vector<int> parent, depth;
explicit LCA(int n = 0) : n(n) {
assert(0 <= n && n <= std::numeric_limits<int>::max() / 2);
edges.reserve(std::max(0, n - 1));
}
int add_edge(int u, int v) {
assert(0 <= u && u < n && 0 <= v && v < n);
edges.emplace_back(u, v);
return static_cast<int>(edges.size()) - 1;
}
void build(int root = 0) {
assert(n >= 0 && edges.size() == std::size_t(std::max(0, n - 1)));
assert(!n || (0 <= root && root < n));
in.resize(n);
out.resize(n);
order.resize(n);
parent.assign(n, -1);
depth.assign(n, 0);
table = StaticRangeMin<int, true>();
if (!n) return;
std::vector<int> degree(n), neighbors(n);
for (auto [u, v] : edges) {
++degree[u], ++degree[v];
neighbors[u] ^= v, neighbors[v] ^= u;
}
degree[root] += 2;
std::fill(out.begin(), out.end(), 1);
int count = 0;
for (int v = 0; v < n; ++v) if (degree[v] == 1) order[count++] = v;
for (int i = 0; i < count; ++i) {
int v = order[i], p = neighbors[v];
parent[v] = p;
out[p] += out[v];
neighbors[p] ^= v;
if (--degree[p] == 1) order[count++] = p;
}
assert(count == n - 1);
finish(root);
}
void build_from_parents(std::vector<int> parents) {
assert(parents.size() <= std::size_t(std::numeric_limits<int>::max() / 2));
n = static_cast<int>(parents.size());
parent = std::move(parents);
in.resize(n);
out.assign(n, 1);
order.resize(n);
depth.assign(n, 0);
table = StaticRangeMin<int, true>();
edges.clear();
edges.reserve(std::max(0, n - 1));
if (!n) return;
int root = -1;
bool ordered = true;
for (int v = 0; v < n; ++v) {
int p = parent[v];
assert(-1 <= p && p < n && p != v);
if (p == -1) {
assert(root == -1);
root = v;
} else {
edges.emplace_back(p, v);
ordered &= p < v;
}
}
assert(root != -1);
if (ordered) {
for (int v = n - 1; v > 0; --v) {
out[parent[v]] += out[v];
order[n - 1 - v] = v;
}
} else {
std::fill(in.begin(), in.end(), 0);
for (int v = 0; v < n; ++v) if (v != root) ++in[parent[v]];
int count = 0;
for (int v = 0; v < n; ++v) if (!in[v] && v != root) order[count++] = v;
for (int i = 0; i < count; ++i) {
int v = order[i], p = parent[v];
out[p] += out[v];
if (!--in[p] && p != root) order[count++] = p;
}
assert(count == n - 1);
}
finish(root);
}
int lca(int u, int v) const {
check(u);
check(v);
int l = in[u], r = in[v];
if (l == r) return u;
if (l > r) std::swap(l, r);
++l;
int w = table.prod(l, r + 1);
return ordered_ ? w : order[w];
}
int dist(int u, int v) const {
int w = lca(u, v);
return depth[u] + depth[v] - 2 * depth[w];
}
bool is_ancestor(int u, int v) const {
check(u);
check(v);
return in[u] <= in[v] && in[v] < out[u];
}
std::pair<int, int> subtree(int v) const {
check(v);
return {in[v], out[v]};
}
};