Skip to content

centroid_decomposition.hpp

SECTIONGraph INCLUDEnoya/graph/centroid_decomposition.hpp

反复选重心,把树拆成较小子树,得到点分治层次;适合统计树上距离和经过重心的路径。

Complexity: Time: \(O(n \log n)\). Space: \(O(n)\); initial DFS has \(O(\text{tree height})\) call depth.

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include。

/// @complexity Time: O(n log n). Space: O(n); initial DFS has O(tree height) call depth.

#include <vector>

namespace noya {

/// @brief Centroid-decomposition tree of an undirected tree (empty is allowed).
/// Input is unchanged; adjacency entries must form a tree with valid vertex IDs.
struct centroid_decomposition {
  int n = 0;
  int rt = -1;
  std::vector<int> fa;
  std::vector<int> dep;
  std::vector<int> ord;

  centroid_decomposition() = default;
  explicit centroid_decomposition(const std::vector<std::vector<int>> &g) {
    build(g);
  }

  /// @brief Rebuild; fa[rt] = -1, dep[rt] = 0, ord is decomposition preorder.
  /// Of two possible centroids choose the one nearer the component entry
  /// (initially 0); visit child components in the centroid's adjacency order.
  void build(const std::vector<std::vector<int>> &g) {
    n = int(g.size());
    fa.assign(n, -1);
    dep.assign(n, -1);
    ord.clear();
    ord.reserve(n);
    if (n == 0) {
      rt = -1;
      return;
    }
    std::vector<int> size(n, 1);
    auto init = [&](auto &self, int u, int p) -> void {
      for (int v : g[u]) if (v != p) {
        self(self, v, u);
        size[u] += size[v];
      }
    };
    init(init, 0, -1);
    auto dfs = [&](auto &self, int entry, int parent, int depth) -> int {
      int total = size[entry], center = entry;
      if (total > 1) {
        for (;;) {
          int next = -1;
          for (int v : g[center]) {
            if (dep[v] == -1 && size[v] > total / 2) {
              next = v;
              break;
            }
          }
          if (next == -1) break;
          // Reroot the component across this edge. After deleting its center,
          // every neighboring component already has correct subtree sizes.
          size[center] -= size[next];
          size[next] += size[center];
          center = next;
        }
      }
      fa[center] = parent;
      dep[center] = depth;
      ord.push_back(center);
      // The decomposition itself has logarithmic depth.
      if (total > 1)
        for (int v : g[center])
          if (dep[v] == -1) self(self, v, center, depth + 1);
      return center;
    };
    rt = dfs(dfs, 0, -1, 0);
  }
};

} // namespace noya

 // NOYA_CENTROID_DECOMPOSITION_HPP
#ifndef NOYA_CENTROID_DECOMPOSITION_HPP
#define NOYA_CENTROID_DECOMPOSITION_HPP 1

/// @complexity Time: O(n log n). Space: O(n); initial DFS has O(tree height) call depth.

#include <vector>

namespace noya {

/// @brief Centroid-decomposition tree of an undirected tree (empty is allowed).
/// Input is unchanged; adjacency entries must form a tree with valid vertex IDs.
struct centroid_decomposition {
  int n = 0;
  int rt = -1;
  std::vector<int> fa;
  std::vector<int> dep;
  std::vector<int> ord;

  centroid_decomposition() = default;
  explicit centroid_decomposition(const std::vector<std::vector<int>> &g) {
    build(g);
  }

  /// @brief Rebuild; fa[rt] = -1, dep[rt] = 0, ord is decomposition preorder.
  /// Of two possible centroids choose the one nearer the component entry
  /// (initially 0); visit child components in the centroid's adjacency order.
  void build(const std::vector<std::vector<int>> &g) {
    n = int(g.size());
    fa.assign(n, -1);
    dep.assign(n, -1);
    ord.clear();
    ord.reserve(n);
    if (n == 0) {
      rt = -1;
      return;
    }
    std::vector<int> size(n, 1);
    auto init = [&](auto &self, int u, int p) -> void {
      for (int v : g[u]) if (v != p) {
        self(self, v, u);
        size[u] += size[v];
      }
    };
    init(init, 0, -1);
    auto dfs = [&](auto &self, int entry, int parent, int depth) -> int {
      int total = size[entry], center = entry;
      if (total > 1) {
        for (;;) {
          int next = -1;
          for (int v : g[center]) {
            if (dep[v] == -1 && size[v] > total / 2) {
              next = v;
              break;
            }
          }
          if (next == -1) break;
          // Reroot the component across this edge. After deleting its center,
          // every neighboring component already has correct subtree sizes.
          size[center] -= size[next];
          size[next] += size[center];
          center = next;
        }
      }
      fa[center] = parent;
      dep[center] = depth;
      ord.push_back(center);
      // The decomposition itself has logarithmic depth.
      if (total > 1)
        for (int v : g[center])
          if (dep[v] == -1) self(self, v, center, depth + 1);
      return center;
    };
    rt = dfs(dfs, 0, -1, 0);
  }
};

} // namespace noya

#endif // NOYA_CENTROID_DECOMPOSITION_HPP
/// @complexity Time: O(n log n). Space: O(n); initial DFS has O(tree height) call depth.

#include <vector>

namespace noya {

/// @brief Centroid-decomposition tree of an undirected tree (empty is allowed).
/// Input is unchanged; adjacency entries must form a tree with valid vertex IDs.
struct centroid_decomposition {
  int n = 0;
  int rt = -1;
  std::vector<int> fa;
  std::vector<int> dep;
  std::vector<int> ord;

  centroid_decomposition() = default;
  explicit centroid_decomposition(const std::vector<std::vector<int>> &g) {
    build(g);
  }

  /// @brief Rebuild; fa[rt] = -1, dep[rt] = 0, ord is decomposition preorder.
  /// Of two possible centroids choose the one nearer the component entry
  /// (initially 0); visit child components in the centroid's adjacency order.
  void build(const std::vector<std::vector<int>> &g) {
    n = int(g.size());
    fa.assign(n, -1);
    dep.assign(n, -1);
    ord.clear();
    ord.reserve(n);
    if (n == 0) {
      rt = -1;
      return;
    }
    std::vector<int> size(n, 1);
    auto init = [&](auto &self, int u, int p) -> void {
      for (int v : g[u]) if (v != p) {
        self(self, v, u);
        size[u] += size[v];
      }
    };
    init(init, 0, -1);
    auto dfs = [&](auto &self, int entry, int parent, int depth) -> int {
      int total = size[entry], center = entry;
      if (total > 1) {
        for (;;) {
          int next = -1;
          for (int v : g[center]) {
            if (dep[v] == -1 && size[v] > total / 2) {
              next = v;
              break;
            }
          }
          if (next == -1) break;
          // Reroot the component across this edge. After deleting its center,
          // every neighboring component already has correct subtree sizes.
          size[center] -= size[next];
          size[next] += size[center];
          center = next;
        }
      }
      fa[center] = parent;
      dep[center] = depth;
      ord.push_back(center);
      // The decomposition itself has logarithmic depth.
      if (total > 1)
        for (int v : g[center])
          if (dep[v] == -1) self(self, v, center, depth + 1);
      return center;
    };
    rt = dfs(dfs, 0, -1, 0);
  }
};

} // namespace noya

 // NOYA_CENTROID_DECOMPOSITION_HPP