Skip to content

two_sat.hpp

SECTIONGraph INCLUDEnoya/graph/two_sat.hpp

追加二元析取约束,solve 每次从完整公式重新求解并返回独立布尔赋值;无解返回 nullopt,零变量返回有值的空数组。同变量恒真式不加边,单文字只加一条蕴含边,递归 Tarjan 直接比较逆拓扑分量编号。

Complexity: Time: \(O(V + C)\) per solve for \(V\) variables and \(C\) clauses; amortized \(O(1)\) per add_clause. Space: \(O(V + C)\), including \(O(V)\) call depth.

AC 记录:two_sat。

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(V + C) per solve for V variables and C clauses;
/// amortized O(1) per add_clause. Space: O(V + C), including O(V) call depth.

#include "atcoder/internal_csr.hpp"

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

namespace noya {

/// @brief Append clauses (x_a == x) OR (x_b == y), then solve from scratch.
/// Variables are in [0,n); require 2*n and 2*clause_count to fit int.
/// Repeated solve() preserves the formula and returns an independent assignment.
/// Recursive Tarjan uses the repository's 512 MiB test stack budget.
class two_sat {
public:
  explicit two_sat(int n) : n_(n) {
    assert(0 <= n && n <= std::numeric_limits<int>::max() / 2);
  }

  void add_clause(int a, bool x, int b, bool y) {
    assert(0 <= a && a < n_ && 0 <= b && b < n_);
    assert(edges_.size() <= std::size_t(std::numeric_limits<int>::max()) - 2);
    if (a == b) {
      if (x == y) edges_.emplace_back(2 * a + !x, 2 * a + x);
      return;
    }
    edges_.emplace_back(2 * a + !x, 2 * b + y);
    edges_.emplace_back(2 * b + !y, 2 * a + x);
  }

  /// Return one satisfying assignment, or nullopt if none exists.
  std::optional<std::vector<bool>> solve() {
    int n = 2 * n_;
    std::vector<int> ids(n);
    {
      atcoder::internal::csr<int> g(n, edges_);
      std::vector<int> ord(n, -1), stack;
      stack.reserve(n);
      int now = 0, count = 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;
            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<bool> answer(n_);
    for (int v = 0; v < n_; ++v) {
      if (ids[2 * v] == ids[2 * v + 1]) return std::nullopt;
      // Components are numbered in reverse topological order.
      answer[v] = ids[2 * v] > ids[2 * v + 1];
    }
    return answer;
  }

  int variable_count() const { return n_; }

private:
  int n_;
  std::vector<std::pair<int, int>> edges_;
};

} // namespace noya

 // NOYA_TWO_SAT_HPP
#ifndef NOYA_TWO_SAT_HPP
#define NOYA_TWO_SAT_HPP 1

/// @complexity Time: O(V + C) per solve for V variables and C clauses;
/// amortized O(1) per add_clause. Space: O(V + C), including O(V) call depth.

#include "atcoder/internal_csr.hpp"

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

namespace noya {

/// @brief Append clauses (x_a == x) OR (x_b == y), then solve from scratch.
/// Variables are in [0,n); require 2*n and 2*clause_count to fit int.
/// Repeated solve() preserves the formula and returns an independent assignment.
/// Recursive Tarjan uses the repository's 512 MiB test stack budget.
class two_sat {
public:
  explicit two_sat(int n) : n_(n) {
    assert(0 <= n && n <= std::numeric_limits<int>::max() / 2);
  }

  void add_clause(int a, bool x, int b, bool y) {
    assert(0 <= a && a < n_ && 0 <= b && b < n_);
    assert(edges_.size() <= std::size_t(std::numeric_limits<int>::max()) - 2);
    if (a == b) {
      if (x == y) edges_.emplace_back(2 * a + !x, 2 * a + x);
      return;
    }
    edges_.emplace_back(2 * a + !x, 2 * b + y);
    edges_.emplace_back(2 * b + !y, 2 * a + x);
  }

  /// Return one satisfying assignment, or nullopt if none exists.
  std::optional<std::vector<bool>> solve() {
    int n = 2 * n_;
    std::vector<int> ids(n);
    {
      atcoder::internal::csr<int> g(n, edges_);
      std::vector<int> ord(n, -1), stack;
      stack.reserve(n);
      int now = 0, count = 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;
            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<bool> answer(n_);
    for (int v = 0; v < n_; ++v) {
      if (ids[2 * v] == ids[2 * v + 1]) return std::nullopt;
      // Components are numbered in reverse topological order.
      answer[v] = ids[2 * v] > ids[2 * v + 1];
    }
    return answer;
  }

  int variable_count() const { return n_; }

private:
  int n_;
  std::vector<std::pair<int, int>> edges_;
};

} // namespace noya

#endif // NOYA_TWO_SAT_HPP
/// @complexity Time: O(V + C) per solve for V variables and C clauses;
/// amortized O(1) per add_clause. Space: O(V + C), including O(V) call depth.

#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 <optional>
#include <utility>
#include <vector>

namespace noya {

/// @brief Append clauses (x_a == x) OR (x_b == y), then solve from scratch.
/// Variables are in [0,n); require 2*n and 2*clause_count to fit int.
/// Repeated solve() preserves the formula and returns an independent assignment.
/// Recursive Tarjan uses the repository's 512 MiB test stack budget.
class two_sat {
public:
  explicit two_sat(int n) : n_(n) {
    assert(0 <= n && n <= std::numeric_limits<int>::max() / 2);
  }

  void add_clause(int a, bool x, int b, bool y) {
    assert(0 <= a && a < n_ && 0 <= b && b < n_);
    assert(edges_.size() <= std::size_t(std::numeric_limits<int>::max()) - 2);
    if (a == b) {
      if (x == y) edges_.emplace_back(2 * a + !x, 2 * a + x);
      return;
    }
    edges_.emplace_back(2 * a + !x, 2 * b + y);
    edges_.emplace_back(2 * b + !y, 2 * a + x);
  }

  /// Return one satisfying assignment, or nullopt if none exists.
  std::optional<std::vector<bool>> solve() {
    int n = 2 * n_;
    std::vector<int> ids(n);
    {
      atcoder::internal::csr<int> g(n, edges_);
      std::vector<int> ord(n, -1), stack;
      stack.reserve(n);
      int now = 0, count = 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;
            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<bool> answer(n_);
    for (int v = 0; v < n_; ++v) {
      if (ids[2 * v] == ids[2 * v + 1]) return std::nullopt;
      // Components are numbered in reverse topological order.
      answer[v] = ids[2 * v] > ids[2 * v + 1];
    }
    return answer;
  }

  int variable_count() const { return n_; }

private:
  int n_;
  std::vector<std::pair<int, int>> edges_;
};

} // namespace noya

 // NOYA_TWO_SAT_HPP