競技プログラミング用 C++ ライブラリ
tree/tree_mo.hpp · GitHub で見る
共通ページ
木の経路に含まれる頂点・辺を追加/削除して、経路上の異なる値の個数や頻度などを一括計算します。木と値は処理中に変更しません。集約は経路をたどる順番によらないものを扱います。
#include "lib/tree/tree_mo.hpp"
#include <vector>
int main() {
std::vector<std::vector<int>> graph{ {1, 2}, {0, 3, 4}, {0}, {1}, {1} };
std::vector<int> value{1, 2, 3, 4, 5}, answers(3);
TreeMo mo(graph);
mo.add_query(3, 2);
mo.add_query(3, 4);
mo.add_query(2, 2);
int sum = 0;
mo.run([&](int v) { sum += value[v]; },
[&](int v) { sum -= value[v]; },
[&](int id) { answers[id] = sum; });
// answers = {10, 11, 3}
}
| 操作 | 内容・返り値 |
|---|---|
TreeMo(graph,root=0,edge_values=false) |
std::vector<std::vector<int>> の無向木から構築 |
size() / query_count()
|
頂点数・問い合わせ数を int で返す |
parent(v) |
指定した根に対する親を int で返す。根は -1
|
add_query(u,v) |
u と v を結ぶ経路を登録し、0 始まりの問い合わせ ID を int で返す |
order(block_size=0) |
処理順の問い合わせ ID を std::vector<int> で返す |
run(add,erase,answer,block_size=0) |
コールバックを呼んで処理。返り値は void
|
頂点番号は 0 始まりです。入力は自己ループ・多重辺のない連結な無向木とし、各辺を両方向の隣接リストに入れます。頂点数は INT_MAX/2 以下、空でない場合の root・u・v は [0,size()) にします。空の木も構築できますが、経路は登録できません。空の木では root を使いません。問い合わせ数は INT_MAX 以下です。
edge_values=false では両端を含む経路上の頂点を扱います。u==v の経路は 1 頂点です。edge_values=true では経路上の辺を扱い、コールバックにはその辺の子側の頂点番号を渡します。u==v の経路は空です。
辺値の場合は、上の例の mo と同じ木に対して次のように使えます。
TreeMo edges(graph, 0, true);
std::vector<int> weight{0, 10, 20, 30, 40};
// weight[v] は parent(v) と v を結ぶ辺の値。根の値は使いません。
edges.add_query(3, 2);
int edge_sum = 0, answer = 0;
edges.run([&](int v) { edge_sum += weight[v]; },
[&](int v) { edge_sum -= weight[v]; },
[&](int) { answer = edge_sum; });
// answer = 60
run の前に集約を空の状態にしてください。add(v) は頂点または辺を追加し、erase(v) は削除します。answer(id) の時点で、集約がその経路と一致します。LCA の追加・除外を呼び出し側で行う必要はありません。
コールバックの追加・削除順は経路上の順番とは限りません。非可換な経路積には Path Product を使ってください。処理中に問い合わせを追加しないでください。再実行は可能ですが、集約を空へ初期化する必要があります。終了時の集約を空へ戻すことは保証しません。
block_size は非負とし、0 では 通常の Mo’s Algorithm と同様に自動選択します。問い合わせがなければコールバックを呼びません。
頂点数を \(N\)、問い合わせ数を \(Q\) として、構築は \(O(N\log N)\) 時間・空間、経路の登録は償却 \(O(1)\)、順序生成は \(O(Q)\) です。コールバックが定数時間なら、自動選択時の処理は \(O(N\sqrt Q+Q)\) 時間、追加の作業空間は \(O(N+Q)\) です。幅 \(B\) を指定する場合の処理時間は \(O(QB+N^2/B+Q)\) です。
#ifndef TREE_MO_HPP
#define TREE_MO_HPP
#include "../data_structure/mo_order.hpp"
#include "lca.hpp"
class TreeMo {
LCA lca_;
std::vector<int> enter_, leave_, tour_, extra_;
std::vector<std::pair<int, int>> ranges_;
bool edge_values_;
public:
explicit TreeMo(const std::vector<std::vector<int>> &graph, int root = 0, bool edge_values = false)
: lca_(), edge_values_(edge_values) {
assert(graph.size() <= std::size_t(std::numeric_limits<int>::max() / 2));
int n = static_cast<int>(graph.size());
enter_.resize(n), leave_.resize(n);
if (!n) return;
assert(0 <= root && root < n);
std::size_t edges = 0;
for (const auto &row : graph) edges += row.size();
assert(edges == std::size_t(n - 1) * 2);
std::vector<int> parents(n, -2), stack{root};
std::vector<std::size_t> next(n);
parents[root] = -1;
tour_.reserve(std::size_t(n) * 2);
tour_.push_back(root);
while (!stack.empty()) {
int v = stack.back();
if (next[v] < graph[v].size()) {
int to = graph[v][next[v]++];
assert(0 <= to && to < n);
if (to == parents[v]) continue;
assert(parents[to] == -2);
parents[to] = v;
enter_[to] = static_cast<int>(tour_.size());
tour_.push_back(to);
stack.push_back(to);
} else {
leave_[v] = static_cast<int>(tour_.size());
tour_.push_back(v);
stack.pop_back();
}
}
assert(tour_.size() == std::size_t(n) * 2);
lca_.build_from_parents(std::move(parents));
}
int size() const { return lca_.n; }
int query_count() const { return static_cast<int>(ranges_.size()); }
int parent(int v) const {
assert(0 <= v && v < size());
return lca_.parent[v];
}
int add_query(int u, int v) {
assert(0 <= u && u < size() && 0 <= v && v < size());
assert(ranges_.size() < std::size_t(std::numeric_limits<int>::max()));
if (enter_[u] > enter_[v]) std::swap(u, v);
int w = lca_.lca(u, v);
if (w == u) {
ranges_.emplace_back(enter_[u] + int(edge_values_), enter_[v] + 1);
extra_.push_back(-1);
} else {
ranges_.emplace_back(leave_[u], enter_[v] + 1);
extra_.push_back(edge_values_ ? -1 : w);
}
return query_count() - 1;
}
std::vector<int> order(int block_size = 0) const { return mo_order(ranges_, block_size); }
template<class Add, class Erase, class Answer>
void run(Add add, Erase erase, Answer answer, int block_size = 0) const {
if (!query_count()) { assert(block_size >= 0); return; }
std::vector<unsigned char> active(size());
auto toggle = [&](int v) {
if (active[v]) erase(v);
else add(v);
active[v] ^= 1;
};
int l = 0, r = 0;
for (int id : order(block_size)) {
auto [a, b] = ranges_[id];
while (a < l) toggle(tour_[--l]);
while (r < b) toggle(tour_[r++]);
while (l < a) toggle(tour_[l++]);
while (b < r) toggle(tour_[--r]);
if (extra_[id] >= 0) toggle(extra_[id]);
answer(id);
if (extra_[id] >= 0) toggle(extra_[id]);
}
}
};
#endif
#line 1 "tree/tree_mo.hpp"
#line 1 "data_structure/mo_order.hpp"
#line 1 "components/radix_sort.hpp"
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
#include <vector>
template <int Bits, class T, class Key>
void stable_radix_sort(std::vector<T> &values, Key &&key) {
static_assert(1 <= Bits && Bits <= 16);
constexpr unsigned buckets = 1U << Bits, mask = buckets - 1;
using K = std::remove_cvref_t<decltype(key(std::declval<const T &>()))>;
static_assert(std::is_integral_v<K> && std::is_unsigned_v<K> && sizeof(K) <= sizeof(std::uint64_t));
if (values.size() < 2) return;
if (values.size() < 256) {
std::stable_sort(values.begin(), values.end(), [&](const T &a, const T &b) { return key(a) < key(b); });
return;
}
std::uint64_t first = key(values.front()), previous = first, varying = 0;
bool sorted = true;
for (std::size_t i = 1; i < values.size(); ++i) {
std::uint64_t current = key(values[i]);
varying |= first ^ current;
sorted &= previous <= current;
previous = current;
}
if (sorted) return;
std::vector<T> scratch(values.size());
using Counts = std::conditional_t<(Bits <= 11), std::array<std::size_t, buckets>, std::vector<std::size_t>>;
Counts count = [] {
if constexpr (Bits <= 11) return Counts{};
else return Counts(buckets);
}();
for (int shift = 0; shift < 64; shift += Bits) {
if (!((varying >> shift) & mask)) continue;
std::fill(count.begin(), count.end(), 0);
for (const auto &value : values) ++count[(static_cast<std::uint64_t>(key(value)) >> shift) & mask];
std::size_t sum = 0;
for (auto &x : count) {
auto n = x;
x = sum, sum += n;
}
for (auto &&value : values) scratch[count[(static_cast<std::uint64_t>(key(value)) >> shift) & mask]++] = std::move(value);
values.swap(scratch);
}
}
template <class T, class Key>
void stable_radix_sort(std::vector<T> &values, Key key) {
stable_radix_sort<8>(values, std::move(key));
}
namespace radix_sort_detail {
template <int Bits = 8, class T, class Key>
void sort_by_key(std::vector<T> &values, Key key) {
using K = std::remove_cvref_t<decltype(key(std::declval<const T &>()))>;
if constexpr (std::is_integral_v<K> && !std::is_same_v<K, bool> && sizeof(K) <= 8 && std::is_default_constructible_v<T>) {
using U = std::make_unsigned_t<K>;
constexpr U sign = std::is_signed_v<K> ? U(1) << (sizeof(K) * 8 - 1) : U(0);
stable_radix_sort<Bits>(values, [&](const T &value) { return U(U(key(value)) ^ sign); });
} else std::sort(values.begin(), values.end(), [&](const T &a, const T &b) { return key(a) < key(b); });
}
}
#line 6 "data_structure/mo_order.hpp"
#include <cassert>
#include <cmath>
#line 9 "data_structure/mo_order.hpp"
#include <limits>
#line 12 "data_structure/mo_order.hpp"
inline std::vector<int> mo_order(const std::vector<std::pair<int, int>>& ranges, int block_size = 0) {
assert(ranges.size() <= std::size_t(std::numeric_limits<int>::max()) && block_size >= 0);
if (ranges.empty()) return {};
int min_l = ranges[0].first, max_l = min_l, min_r = ranges[0].second, max_r = min_r;
for (auto [l, r] : ranges) {
assert(0 <= l && l <= r);
min_l = std::min(min_l, l), max_l = std::max(max_l, l);
min_r = std::min(min_r, r), max_r = std::max(max_r, r);
}
if (!block_size) block_size = std::max(1, int(std::sqrt((double(max_l) - min_l + 1) * (double(max_r) - min_r + 1) / ranges.size())));
std::vector<std::pair<std::uint64_t, int>> keys;
keys.reserve(ranges.size());
for (int i = 0; i < int(ranges.size()); ++i) {
auto [l, r] = ranges[i];
std::uint32_t block = (l - min_l) / block_size;
std::uint32_t right = block & 1 ? ~std::uint32_t(r) : std::uint32_t(r);
keys.emplace_back((std::uint64_t(block) << 32) | right, i);
}
if (keys.size() < 2048) std::sort(keys.begin(), keys.end());
else stable_radix_sort(keys, [](const auto &x) { return x.first; });
std::vector<int> order;
order.reserve(keys.size());
for (auto [key, i] : keys) { (void)key; order.push_back(i); }
return order;
}
#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 6 "data_structure/sparse_table.hpp"
#include <bit>
#line 14 "data_structure/sparse_table.hpp"
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 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]};
}
};
#line 6 "tree/tree_mo.hpp"
class TreeMo {
LCA lca_;
std::vector<int> enter_, leave_, tour_, extra_;
std::vector<std::pair<int, int>> ranges_;
bool edge_values_;
public:
explicit TreeMo(const std::vector<std::vector<int>> &graph, int root = 0, bool edge_values = false)
: lca_(), edge_values_(edge_values) {
assert(graph.size() <= std::size_t(std::numeric_limits<int>::max() / 2));
int n = static_cast<int>(graph.size());
enter_.resize(n), leave_.resize(n);
if (!n) return;
assert(0 <= root && root < n);
std::size_t edges = 0;
for (const auto &row : graph) edges += row.size();
assert(edges == std::size_t(n - 1) * 2);
std::vector<int> parents(n, -2), stack{root};
std::vector<std::size_t> next(n);
parents[root] = -1;
tour_.reserve(std::size_t(n) * 2);
tour_.push_back(root);
while (!stack.empty()) {
int v = stack.back();
if (next[v] < graph[v].size()) {
int to = graph[v][next[v]++];
assert(0 <= to && to < n);
if (to == parents[v]) continue;
assert(parents[to] == -2);
parents[to] = v;
enter_[to] = static_cast<int>(tour_.size());
tour_.push_back(to);
stack.push_back(to);
} else {
leave_[v] = static_cast<int>(tour_.size());
tour_.push_back(v);
stack.pop_back();
}
}
assert(tour_.size() == std::size_t(n) * 2);
lca_.build_from_parents(std::move(parents));
}
int size() const { return lca_.n; }
int query_count() const { return static_cast<int>(ranges_.size()); }
int parent(int v) const {
assert(0 <= v && v < size());
return lca_.parent[v];
}
int add_query(int u, int v) {
assert(0 <= u && u < size() && 0 <= v && v < size());
assert(ranges_.size() < std::size_t(std::numeric_limits<int>::max()));
if (enter_[u] > enter_[v]) std::swap(u, v);
int w = lca_.lca(u, v);
if (w == u) {
ranges_.emplace_back(enter_[u] + int(edge_values_), enter_[v] + 1);
extra_.push_back(-1);
} else {
ranges_.emplace_back(leave_[u], enter_[v] + 1);
extra_.push_back(edge_values_ ? -1 : w);
}
return query_count() - 1;
}
std::vector<int> order(int block_size = 0) const { return mo_order(ranges_, block_size); }
template<class Add, class Erase, class Answer>
void run(Add add, Erase erase, Answer answer, int block_size = 0) const {
if (!query_count()) { assert(block_size >= 0); return; }
std::vector<unsigned char> active(size());
auto toggle = [&](int v) {
if (active[v]) erase(v);
else add(v);
active[v] ^= 1;
};
int l = 0, r = 0;
for (int id : order(block_size)) {
auto [a, b] = ranges_[id];
while (a < l) toggle(tour_[--l]);
while (r < b) toggle(tour_[r++]);
while (l < a) toggle(tour_[l++]);
while (b < r) toggle(tour_[--r]);
if (extra_[id] >= 0) toggle(extra_[id]);
answer(id);
if (extra_[id] >= 0) toggle(extra_[id]);
}
}
};