cplib-cpp

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub hitonanode/cplib-cpp

:heavy_check_mark: Lowest common ancestor of tree based on sparse table (クエリ $O(1)$ の最小共通祖先)
(tree/lca_rmq.hpp)

根を固定した木に対する 2 頂点の最小共通祖先,および 2 頂点間の距離の計算.前処理 $O(N \log N)$, 空間 $O(N \log N)$, クエリ $O(1)$.

使用方法

add_edge(int u, int v) を $N - 1$ 回行って木を構築した後,build(int root) によって根を指定する.

TreeLCA tree(N);
for (int e = 0; e < N - 1; e++) {
    int u, v;
    cin >> u >> v;
    tree.add_edge(u, v);
}

tree.build(0);

cout << tree.lca(a, b) << '\n'; // (a, b) の最長共通祖先
cout << tree.path_length(a, b) << '\n'; // 2 頂点 a, b の距離

問題例

Depends on

Verified with

Code

#pragma once
#include "../sparse_table/rmq_sparse_table.hpp"
#include <algorithm>
#include <cassert>
#include <utility>
#include <vector>

struct TreeLCA {
    const int N;
    std::vector<std::vector<int>> to;
    int root;
    TreeLCA(int V = 0) : N(V), to(V), root(-1) {}

    void add_edge(int u, int v) {
        assert(0 <= u and u < N);
        assert(0 <= v and v < N);
        assert(u != v);
        to[u].push_back(v);
        to[v].push_back(u);
    }

    using P = std::pair<int, int>;
    std::vector<int> subtree_begin;
    std::vector<P> vis_order;
    std::vector<int> depth;
    void _build_dfs(int now, int prv, int dnow) {
        subtree_begin[now] = vis_order.size();
        vis_order.emplace_back(dnow, now);
        depth[now] = dnow;
        for (auto &&nxt : to[now]) {
            if (nxt != prv) {
                _build_dfs(nxt, now, dnow + 1);
                vis_order.emplace_back(dnow, now);
            }
        }
    }

    StaticRMQ<P> rmq;
    void build(int root_) {
        assert(root_ >= 0 and root_ < N);
        if (root == root_) return;
        root = root_;
        subtree_begin.assign(N, 0);
        vis_order.clear();
        vis_order.reserve(N * 2);
        depth.assign(N, 0);
        _build_dfs(root, -1, 0);
        rmq = {vis_order, P{N, -1}};
    }

    bool built() const noexcept { return root >= 0; }

    int lca(int u, int v) const {
        assert(0 <= u and u < N);
        assert(0 <= v and v < N);
        assert(built());

        int a = subtree_begin[u], b = subtree_begin[v];
        if (a > b) std::swap(a, b);
        return rmq.get(a, b + 1).second;
    };

    int path_length(int u, int v) const { return depth[u] + depth[v] - depth[lca(u, v)] * 2; }
};
#line 2 "sparse_table/rmq_sparse_table.hpp"
#include <algorithm>
#include <cassert>
#include <vector>

// CUT begin
// Range Minimum Query for static sequence by sparse table
// Complexity: $O(N \log N)$ for precalculation, $O(1)$ per query
template <typename T> struct StaticRMQ {
    inline T func(const T &l, const T &r) const noexcept { return std::min<T>(l, r); }
    int N, lgN;
    T defaultT;
    std::vector<std::vector<T>> data;
    std::vector<int> lgx_table;
    StaticRMQ() = default;
    StaticRMQ(const std::vector<T> &sequence, T defaultT)
        : N(sequence.size()), defaultT(defaultT) {
        lgx_table.resize(N + 1);
        for (int i = 2; i < N + 1; i++) lgx_table[i] = lgx_table[i >> 1] + 1;
        lgN = lgx_table[N] + 1;
        data.assign(lgN, std::vector<T>(N, defaultT));
        data[0] = sequence;
        for (int d = 1; d < lgN; d++) {
            for (int i = 0; i + (1 << d) <= N; i++) {
                data[d][i] = func(data[d - 1][i], data[d - 1][i + (1 << (d - 1))]);
            }
        }
    }
    T get(int l, int r) const { // [l, r), 0-indexed
        assert(l >= 0 and r <= N);
        if (l >= r) return defaultT;
        int d = lgx_table[r - l];
        return func(data[d][l], data[d][r - (1 << d)]);
    }
};
#line 5 "tree/lca_rmq.hpp"
#include <utility>
#line 7 "tree/lca_rmq.hpp"

struct TreeLCA {
    const int N;
    std::vector<std::vector<int>> to;
    int root;
    TreeLCA(int V = 0) : N(V), to(V), root(-1) {}

    void add_edge(int u, int v) {
        assert(0 <= u and u < N);
        assert(0 <= v and v < N);
        assert(u != v);
        to[u].push_back(v);
        to[v].push_back(u);
    }

    using P = std::pair<int, int>;
    std::vector<int> subtree_begin;
    std::vector<P> vis_order;
    std::vector<int> depth;
    void _build_dfs(int now, int prv, int dnow) {
        subtree_begin[now] = vis_order.size();
        vis_order.emplace_back(dnow, now);
        depth[now] = dnow;
        for (auto &&nxt : to[now]) {
            if (nxt != prv) {
                _build_dfs(nxt, now, dnow + 1);
                vis_order.emplace_back(dnow, now);
            }
        }
    }

    StaticRMQ<P> rmq;
    void build(int root_) {
        assert(root_ >= 0 and root_ < N);
        if (root == root_) return;
        root = root_;
        subtree_begin.assign(N, 0);
        vis_order.clear();
        vis_order.reserve(N * 2);
        depth.assign(N, 0);
        _build_dfs(root, -1, 0);
        rmq = {vis_order, P{N, -1}};
    }

    bool built() const noexcept { return root >= 0; }

    int lca(int u, int v) const {
        assert(0 <= u and u < N);
        assert(0 <= v and v < N);
        assert(built());

        int a = subtree_begin[u], b = subtree_begin[v];
        if (a > b) std::swap(a, b);
        return rmq.get(a, b + 1).second;
    };

    int path_length(int u, int v) const { return depth[u] + depth[v] - depth[lca(u, v)] * 2; }
};
Back to top page