Skip to content

strongly_connected_components.hpp

SECTIONGraph INCLUDEnoya/graph/strongly_connected_components.hpp

返回按拓扑序排列的强连通分量列表,分量内点号递增;直接用原边表建立 CSR,递归 Tarjan 返回 low 值,省去中间边表复制与独立 low 数组。

Complexity: Time: \(O(V + E)\). Space: \(O(V + E)\).

AC 记录:scc。

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(V + E).
/// Space: O(V + E).

#include "atcoder/internal_csr.hpp"

#include <algorithm>
#include <cassert>
#include <limits>
#include <utility>
#include <vector>

namespace noya {

/// @brief Decompose a directed graph into strongly connected components in
/// topological order; vertices within each component are increasing.
/// Empty graphs, loops and parallel edges are allowed. Require 0<=n<INT_MAX,
/// valid endpoints and at most INT_MAX edges. Input is unchanged. Preserve
/// ACL's traversal-dependent order between incomparable components.
/// Recursive Tarjan may use O(n) call depth; local tests use 512 MiB stacks.
inline std::vector<std::vector<int>>
strongly_connected_components(int n,
                              const std::vector<std::pair<int, int>> &es) {
  assert(0 <= n && n < std::numeric_limits<int>::max());
  assert(es.size() <= std::size_t(std::numeric_limits<int>::max()));
  for (auto [u, v] : es) {
    assert(0 <= u && u < n && 0 <= v && v < n);
  }
  std::vector<int> ids(n);
  int count = 0;
  { // Release traversal storage before constructing the component vectors.
    atcoder::internal::csr<int> g(n, es);
    std::vector<int> stack, ord(n, -1);
    stack.reserve(n);
    int now = 0;
    auto dfs = [&](auto &self, int v) -> int {
      int low = ord[v] = now++;
      stack.push_back(v);
      for (int i = g.start[v]; i < g.start[v + 1]; ++i) {
        int u = g.elist[i];
        low = std::min(low, ord[u] == -1 ? self(self, u) : ord[u]);
      }
      if (low == ord[v]) {
        while (true) {
          int u = stack.back();
          stack.pop_back();
          ord[u] = n; // Completed components no longer lower an active low-link.
          ids[u] = count;
          if (u == v) break;
        }
        ++count;
      }
      return low;
    };
    for (int v = 0; v < n; ++v)
      if (ord[v] == -1) dfs(dfs, v);
  }
  std::vector<int> sizes(count);
  for (auto &id : ids) {
    id = count - 1 - id;
    ++sizes[id];
  }
  std::vector<std::vector<int>> result(count);
  for (int id = 0; id < count; ++id) result[id].reserve(sizes[id]);
  for (int v = 0; v < n; ++v) result[ids[v]].push_back(v);
  return result;
}

} // namespace noya

 // NOYA_STRONGLY_CONNECTED_COMPONENTS_HPP
#ifndef NOYA_STRONGLY_CONNECTED_COMPONENTS_HPP
#define NOYA_STRONGLY_CONNECTED_COMPONENTS_HPP 1

/// @complexity Time: O(V + E).
/// Space: O(V + E).

#include "atcoder/internal_csr.hpp"

#include <algorithm>
#include <cassert>
#include <limits>
#include <utility>
#include <vector>

namespace noya {

/// @brief Decompose a directed graph into strongly connected components in
/// topological order; vertices within each component are increasing.
/// Empty graphs, loops and parallel edges are allowed. Require 0<=n<INT_MAX,
/// valid endpoints and at most INT_MAX edges. Input is unchanged. Preserve
/// ACL's traversal-dependent order between incomparable components.
/// Recursive Tarjan may use O(n) call depth; local tests use 512 MiB stacks.
inline std::vector<std::vector<int>>
strongly_connected_components(int n,
                              const std::vector<std::pair<int, int>> &es) {
  assert(0 <= n && n < std::numeric_limits<int>::max());
  assert(es.size() <= std::size_t(std::numeric_limits<int>::max()));
  for (auto [u, v] : es) {
    assert(0 <= u && u < n && 0 <= v && v < n);
  }
  std::vector<int> ids(n);
  int count = 0;
  { // Release traversal storage before constructing the component vectors.
    atcoder::internal::csr<int> g(n, es);
    std::vector<int> stack, ord(n, -1);
    stack.reserve(n);
    int now = 0;
    auto dfs = [&](auto &self, int v) -> int {
      int low = ord[v] = now++;
      stack.push_back(v);
      for (int i = g.start[v]; i < g.start[v + 1]; ++i) {
        int u = g.elist[i];
        low = std::min(low, ord[u] == -1 ? self(self, u) : ord[u]);
      }
      if (low == ord[v]) {
        while (true) {
          int u = stack.back();
          stack.pop_back();
          ord[u] = n; // Completed components no longer lower an active low-link.
          ids[u] = count;
          if (u == v) break;
        }
        ++count;
      }
      return low;
    };
    for (int v = 0; v < n; ++v)
      if (ord[v] == -1) dfs(dfs, v);
  }
  std::vector<int> sizes(count);
  for (auto &id : ids) {
    id = count - 1 - id;
    ++sizes[id];
  }
  std::vector<std::vector<int>> result(count);
  for (int id = 0; id < count; ++id) result[id].reserve(sizes[id]);
  for (int v = 0; v < n; ++v) result[ids[v]].push_back(v);
  return result;
}

} // namespace noya

#endif // NOYA_STRONGLY_CONNECTED_COMPONENTS_HPP
/// @complexity Time: O(V + E).
/// Space: O(V + E).

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder

#include <algorithm>
#include <cassert>
#include <limits>
#include <utility>
#include <vector>

namespace noya {

/// @brief Decompose a directed graph into strongly connected components in
/// topological order; vertices within each component are increasing.
/// Empty graphs, loops and parallel edges are allowed. Require 0<=n<INT_MAX,
/// valid endpoints and at most INT_MAX edges. Input is unchanged. Preserve
/// ACL's traversal-dependent order between incomparable components.
/// Recursive Tarjan may use O(n) call depth; local tests use 512 MiB stacks.
inline std::vector<std::vector<int>>
strongly_connected_components(int n,
                              const std::vector<std::pair<int, int>> &es) {
  assert(0 <= n && n < std::numeric_limits<int>::max());
  assert(es.size() <= std::size_t(std::numeric_limits<int>::max()));
  for (auto [u, v] : es) {
    assert(0 <= u && u < n && 0 <= v && v < n);
  }
  std::vector<int> ids(n);
  int count = 0;
  { // Release traversal storage before constructing the component vectors.
    atcoder::internal::csr<int> g(n, es);
    std::vector<int> stack, ord(n, -1);
    stack.reserve(n);
    int now = 0;
    auto dfs = [&](auto &self, int v) -> int {
      int low = ord[v] = now++;
      stack.push_back(v);
      for (int i = g.start[v]; i < g.start[v + 1]; ++i) {
        int u = g.elist[i];
        low = std::min(low, ord[u] == -1 ? self(self, u) : ord[u]);
      }
      if (low == ord[v]) {
        while (true) {
          int u = stack.back();
          stack.pop_back();
          ord[u] = n; // Completed components no longer lower an active low-link.
          ids[u] = count;
          if (u == v) break;
        }
        ++count;
      }
      return low;
    };
    for (int v = 0; v < n; ++v)
      if (ord[v] == -1) dfs(dfs, v);
  }
  std::vector<int> sizes(count);
  for (auto &id : ids) {
    id = count - 1 - id;
    ++sizes[id];
  }
  std::vector<std::vector<int>> result(count);
  for (int id = 0; id < count; ++id) result[id].reserve(sizes[id]);
  for (int v = 0; v < n; ++v) result[ids[v]].push_back(v);
  return result;
}

} // namespace noya

 // NOYA_STRONGLY_CONNECTED_COMPONENTS_HPP