This documentation is automatically generated by online-judge-tools/verification-helper
#include "src/graph/tree/offline-query/offline-query-lowest-common-ancestor.hpp"
Tarjan’s Offline Algorithm によって実装されているオフライン最小共通祖先
OfflineLCAQuery(const G &g)
最小共通祖先を求める木 g
を渡す。
void add_query(usize u, usize v)
頂点 $u$ と $v$ の最小共通祖先 $lca(u, v)$ を求めるクエリを追加する。
後述する build
をした後、lca(u, v)
によって $lca(u, v)$ を得ることができる。
void build(usize root)
頂点 root
を根とする根付き木上での最小共通祖先を計算する。
usize lca(usize u, usize v)
$lca(u, v)$ を返す。根は直近で呼ばれた build(root)
に依存する。
add_query(u, v)
が呼ばれており、かつその後に build(root)
が呼ばれていること。#pragma once
#include "src/cpp-template/header/rep.hpp"
#include "src/cpp-template/header/size-alias.hpp"
#include "src/data-structure/disjoint-set-union.hpp"
#include "src/utility/pair-hash.hpp"
#include <cassert>
#include <unordered_map>
#include <utility>
#include <vector>
namespace luz {
template < class G >
class OfflineLCAQuery {
using graph = G;
using cost_type = typename G::cost_type;
usize g_size;
graph g;
usize query_count;
std::vector< std::vector< usize > > qs;
DisjointSetUnion dsu;
std::vector< bool > visited;
std::vector< usize > ancestors;
using query_type = std::pair< usize, usize >;
std::unordered_map< query_type, usize, PairHash > results;
void bound_check(usize v) const {
assert(v < g_size);
}
void dfs(usize v) {
visited[v] = true;
ancestors[v] = v;
for (const auto &e: g[v]) {
if (visited[e.to]) continue;
dfs(e.to);
dsu.merge(v, e.to);
ancestors[dsu.leader(v)] = v;
}
for (const auto &u: qs[v]) {
if (not visited[u]) continue;
results[query_type(u, v)] = results[query_type(v, u)] =
ancestors[dsu.leader(u)];
}
}
public:
using Queries = std::vector< std::pair< usize, usize > >;
OfflineLCAQuery(G &g)
: g_size(g.size()),
g(g),
query_count(0),
qs(g_size),
dsu(g_size),
visited(g_size, false),
ancestors(g_size) {}
void add_query(usize u, usize v) {
bound_check(u);
bound_check(v);
qs[u].emplace_back(v);
qs[v].emplace_back(u);
query_count++;
}
void build(usize root) {
bound_check(root);
results.reserve(2 * query_count);
dfs(root);
}
usize lca(usize u, usize v) {
bound_check(u);
bound_check(v);
query_type qi(u, v);
assert(results.count(qi));
return results[qi];
}
};
} // namespace luz
#line 2 "src/graph/tree/offline-query/offline-query-lowest-common-ancestor.hpp"
#line 2 "src/cpp-template/header/rep.hpp"
#line 2 "src/cpp-template/header/size-alias.hpp"
#include <cstddef>
namespace luz {
using isize = std::ptrdiff_t;
using usize = std::size_t;
} // namespace luz
#line 4 "src/cpp-template/header/rep.hpp"
#include <algorithm>
namespace luz {
struct rep {
struct itr {
usize i;
constexpr itr(const usize i) noexcept: i(i) {}
void operator++() noexcept {
++i;
}
constexpr usize operator*() const noexcept {
return i;
}
constexpr bool operator!=(const itr x) const noexcept {
return i != x.i;
}
};
const itr f, l;
constexpr rep(const usize f, const usize l) noexcept
: f(std::min(f, l)),
l(l) {}
constexpr auto begin() const noexcept {
return f;
}
constexpr auto end() const noexcept {
return l;
}
};
struct rrep {
struct itr {
usize i;
constexpr itr(const usize i) noexcept: i(i) {}
void operator++() noexcept {
--i;
}
constexpr usize operator*() const noexcept {
return i;
}
constexpr bool operator!=(const itr x) const noexcept {
return i != x.i;
}
};
const itr f, l;
constexpr rrep(const usize f, const usize l) noexcept
: f(l - 1),
l(std::min(f, l) - 1) {}
constexpr auto begin() const noexcept {
return f;
}
constexpr auto end() const noexcept {
return l;
}
};
} // namespace luz
#line 2 "src/data-structure/disjoint-set-union.hpp"
#line 5 "src/data-structure/disjoint-set-union.hpp"
#line 7 "src/data-structure/disjoint-set-union.hpp"
#include <cassert>
#include <vector>
namespace luz {
class DisjointSetUnion {
usize n;
// vals[v] :=
// if v is root node: -1 * component size
// otherwise: parent node
std::vector< isize > vals;
void bound_check(usize v) const {
assert(v < n);
}
usize impl_leader(usize v) {
if (vals[v] < 0) return v;
return vals[v] = leader(vals[v]);
}
public:
DisjointSetUnion() = default;
explicit DisjointSetUnion(usize n): n(n), vals(n, -1) {}
usize size() const {
return n;
}
usize leader(usize v) {
bound_check(v);
return impl_leader(v);
}
bool same(usize u, usize v) {
bound_check(u), bound_check(v);
return impl_leader(u) == impl_leader(v);
}
usize merge(usize u, usize v) {
bound_check(u);
bound_check(v);
isize x = impl_leader(u);
isize y = impl_leader(v);
if (x == y) return x;
if (-vals[x] < -vals[y]) std::swap(x, y);
vals[x] += vals[y];
vals[y] = x;
return x;
}
usize group_size(usize v) {
bound_check(v);
return -vals[impl_leader(v)];
}
std::vector< std::vector< usize > > groups() {
std::vector< std::vector< usize > > result(n);
std::vector< usize > leaders(n), g_sizes(n);
for (usize v: rep(0, n)) {
leaders[v] = impl_leader(v);
g_sizes[leaders[v]]++;
}
for (usize v: rep(0, n)) {
result[v].reserve(g_sizes[v]);
}
for (usize v: rep(0, n)) {
result[leaders[v]].emplace_back(v);
}
auto empty_check = [](const std::vector< usize > &vs) {
return vs.empty();
};
result.erase(
std::remove_if(result.begin(), result.end(), empty_check),
result.end());
return result;
}
};
} // namespace luz
#line 2 "src/utility/pair-hash.hpp"
#line 4 "src/utility/pair-hash.hpp"
#include <functional>
#include <utility>
namespace luz {
class PairHash {
template < typename T >
usize hash_combine(usize hr, const T &x) const {
usize h = std::hash< T >()(x);
return hr ^ (h + (hr << 11) + (hr >> 13));
}
public:
template < typename F, typename S >
usize operator()(const std::pair< F, S > &p) const {
return hash_combine(hash_combine(0, p.first), p.second);
}
};
} // namespace luz
#line 7 "src/graph/tree/offline-query/offline-query-lowest-common-ancestor.hpp"
#line 9 "src/graph/tree/offline-query/offline-query-lowest-common-ancestor.hpp"
#include <unordered_map>
#line 12 "src/graph/tree/offline-query/offline-query-lowest-common-ancestor.hpp"
namespace luz {
template < class G >
class OfflineLCAQuery {
using graph = G;
using cost_type = typename G::cost_type;
usize g_size;
graph g;
usize query_count;
std::vector< std::vector< usize > > qs;
DisjointSetUnion dsu;
std::vector< bool > visited;
std::vector< usize > ancestors;
using query_type = std::pair< usize, usize >;
std::unordered_map< query_type, usize, PairHash > results;
void bound_check(usize v) const {
assert(v < g_size);
}
void dfs(usize v) {
visited[v] = true;
ancestors[v] = v;
for (const auto &e: g[v]) {
if (visited[e.to]) continue;
dfs(e.to);
dsu.merge(v, e.to);
ancestors[dsu.leader(v)] = v;
}
for (const auto &u: qs[v]) {
if (not visited[u]) continue;
results[query_type(u, v)] = results[query_type(v, u)] =
ancestors[dsu.leader(u)];
}
}
public:
using Queries = std::vector< std::pair< usize, usize > >;
OfflineLCAQuery(G &g)
: g_size(g.size()),
g(g),
query_count(0),
qs(g_size),
dsu(g_size),
visited(g_size, false),
ancestors(g_size) {}
void add_query(usize u, usize v) {
bound_check(u);
bound_check(v);
qs[u].emplace_back(v);
qs[v].emplace_back(u);
query_count++;
}
void build(usize root) {
bound_check(root);
results.reserve(2 * query_count);
dfs(root);
}
usize lca(usize u, usize v) {
bound_check(u);
bound_check(v);
query_type qi(u, v);
assert(results.count(qi));
return results[qi];
}
};
} // namespace luz