cplib-cpp

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

View the Project on GitHub hitonanode/cplib-cpp

:heavy_check_mark: Extended block cut tree (Block cut tree の亜種)
(graph/extended_block_cut_trees.hpp)

与えられた $n$ 頂点 $m$ 辺の無向グラフに対する block cut tree (のようなもの)を構築する.計算量は $O(n + m)$.

詳細な仕様は以下の通り.

なお,出力されるグラフは以下の性質を満たす.

使用方法

int n;  // Num. of vertices
vector<pair<int, int>> edges;

const extended_block_cut_trees bct(n, edges);

int b = bct.B;  // Num. of blocks
vector<vector<int>> bct_to = bct.to;
assert(n + b == (int)bct_to.size());

// 得られた block cut tree を heavy-light decomposition などに使う場合
heavy_light_decomposition hld(bct.size(), bct.get_edges());
hld.build();

問題例

参考文献・リンク

Verified with

Code

#pragma once

#include <cassert>
#include <utility>
#include <vector>

// Construct block cut tree (or forest) from a given graph
// Complexity: O(N + M), N = |vertices|, M = |edges|
// based on this idea: https://x.com/noshi91/status/1529858538650374144
// based on this implementation: https://ssrs-cp.github.io/cp_library/graph/extended_block_cut_tree.hpp.html
struct extended_block_cut_trees {
    int N;                            // number of vertices
    int B;                            // number of blocks
    std::vector<std::vector<int>> to; // (0, ..., N - 1): vertices, (N, ..., N + B - 1): blocks

    extended_block_cut_trees(int N, const std::vector<std::pair<int, int>> &edges)
        : N(N), B(0), to(N) {
        std::vector<std::vector<int>> adj(N);
        for (auto [u, v] : edges) {
            if (u != v) adj.at(u).push_back(v), adj.at(v).push_back(u);
        }

        std::vector<int> dfs_next(N, -1), dist(N, -1), back_cnt(N);

        auto rec1 = [&](auto &&self, int now) -> void {
            for (int nxt : adj[now]) {
                if (dist[nxt] == -1) {
                    dist[nxt] = dist[now] + 1;
                    dfs_next[now] = nxt;
                    self(self, nxt);
                    back_cnt[now] += back_cnt[nxt];
                } else if (dist[nxt] < dist[now] - 1) {
                    ++back_cnt[now];
                    --back_cnt[dfs_next[nxt]];
                }
            }
        };

        for (int i = 0; i < N; ++i) {
            if (dist[i] == -1) dist[i] = 0, rec1(rec1, i);
        }

        std::vector<bool> used(N);

        auto rec2 = [&](auto &&self, int now, int current_b) -> void {
            used[now] = true;
            bool ok = false;

            for (int nxt : adj[now]) {
                if (dist[nxt] == dist[now] + 1 and !used[nxt]) {
                    if (back_cnt[nxt] > 0) {
                        if (!ok) {
                            ok = true;
                            add_edge(now, current_b);
                        }
                        self(self, nxt, current_b);
                    } else {
                        to.push_back({});
                        ++B;
                        add_edge(now, B - 1);
                        self(self, nxt, B - 1);
                    }
                }
            }

            if (!ok and dist[now] > 0) { add_edge(now, current_b); }
        };

        for (int i = 0; i < N; ++i) {
            if (dist[i] == 0) { rec2(rec2, i, B - 1); }
            if (adj[i].empty()) {
                to.push_back({});
                ++B;
                add_edge(i, B - 1);
            }
        }
    }

    int size() const { return N + B; }

    bool is_articulation_point(int vertex) const {
        assert(0 <= vertex and vertex < N);
        return to[vertex].size() > 1;
    }

    int block_size(int block) const {
        assert(0 <= block and block < B);
        return to[N + block].size();
    }

    const std::vector<int> &block_vertices(int block) const {
        assert(0 <= block and block < B);
        return to[N + block];
    }

    std::vector<std::vector<int>> biconnected_components() const {
        return std::vector<std::vector<int>>(to.begin() + N, to.end());
    }

    // first < N (vertices), second >= N (blocks)
    std::vector<std::pair<int, int>> get_edges() const {
        std::vector<std::pair<int, int>> edges;
        for (int i = 0; i < N; ++i) {
            for (int j : to[i]) edges.emplace_back(i, j);
        }
        return edges;
    }

private:
    void add_edge(int vertex, int block) {
        assert(0 <= vertex and vertex < N);
        assert(0 <= block and block < B);
        to[vertex].push_back(N + block);
        to[N + block].push_back(vertex);
    }
};
#line 2 "graph/extended_block_cut_trees.hpp"

#include <cassert>
#include <utility>
#include <vector>

// Construct block cut tree (or forest) from a given graph
// Complexity: O(N + M), N = |vertices|, M = |edges|
// based on this idea: https://x.com/noshi91/status/1529858538650374144
// based on this implementation: https://ssrs-cp.github.io/cp_library/graph/extended_block_cut_tree.hpp.html
struct extended_block_cut_trees {
    int N;                            // number of vertices
    int B;                            // number of blocks
    std::vector<std::vector<int>> to; // (0, ..., N - 1): vertices, (N, ..., N + B - 1): blocks

    extended_block_cut_trees(int N, const std::vector<std::pair<int, int>> &edges)
        : N(N), B(0), to(N) {
        std::vector<std::vector<int>> adj(N);
        for (auto [u, v] : edges) {
            if (u != v) adj.at(u).push_back(v), adj.at(v).push_back(u);
        }

        std::vector<int> dfs_next(N, -1), dist(N, -1), back_cnt(N);

        auto rec1 = [&](auto &&self, int now) -> void {
            for (int nxt : adj[now]) {
                if (dist[nxt] == -1) {
                    dist[nxt] = dist[now] + 1;
                    dfs_next[now] = nxt;
                    self(self, nxt);
                    back_cnt[now] += back_cnt[nxt];
                } else if (dist[nxt] < dist[now] - 1) {
                    ++back_cnt[now];
                    --back_cnt[dfs_next[nxt]];
                }
            }
        };

        for (int i = 0; i < N; ++i) {
            if (dist[i] == -1) dist[i] = 0, rec1(rec1, i);
        }

        std::vector<bool> used(N);

        auto rec2 = [&](auto &&self, int now, int current_b) -> void {
            used[now] = true;
            bool ok = false;

            for (int nxt : adj[now]) {
                if (dist[nxt] == dist[now] + 1 and !used[nxt]) {
                    if (back_cnt[nxt] > 0) {
                        if (!ok) {
                            ok = true;
                            add_edge(now, current_b);
                        }
                        self(self, nxt, current_b);
                    } else {
                        to.push_back({});
                        ++B;
                        add_edge(now, B - 1);
                        self(self, nxt, B - 1);
                    }
                }
            }

            if (!ok and dist[now] > 0) { add_edge(now, current_b); }
        };

        for (int i = 0; i < N; ++i) {
            if (dist[i] == 0) { rec2(rec2, i, B - 1); }
            if (adj[i].empty()) {
                to.push_back({});
                ++B;
                add_edge(i, B - 1);
            }
        }
    }

    int size() const { return N + B; }

    bool is_articulation_point(int vertex) const {
        assert(0 <= vertex and vertex < N);
        return to[vertex].size() > 1;
    }

    int block_size(int block) const {
        assert(0 <= block and block < B);
        return to[N + block].size();
    }

    const std::vector<int> &block_vertices(int block) const {
        assert(0 <= block and block < B);
        return to[N + block];
    }

    std::vector<std::vector<int>> biconnected_components() const {
        return std::vector<std::vector<int>>(to.begin() + N, to.end());
    }

    // first < N (vertices), second >= N (blocks)
    std::vector<std::pair<int, int>> get_edges() const {
        std::vector<std::pair<int, int>> edges;
        for (int i = 0; i < N; ++i) {
            for (int j : to[i]) edges.emplace_back(i, j);
        }
        return edges;
    }

private:
    void add_edge(int vertex, int block) {
        assert(0 <= vertex and vertex < N);
        assert(0 <= block and block < B);
        to[vertex].push_back(N + block);
        to[N + block].push_back(vertex);
    }
};
Back to top page