Skip to content

pfaffian.hpp

SECTIONMath INCLUDEnoya/pfaffian.hpp

计算斜对称矩阵的 Pfaffian;用于平方等于行列式的配对计数公式。

\[ \displaystyle \operatorname{pf}(A)^2=\det(A) \]

Complexity: Time: O(n^3). Space: O(n^2).

AC 记录:pfaffian_of_matrix

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n^3).
/// Space: O(n^2).

#include <algorithm>
#include <cassert>
#include <vector>

namespace noya {

/// @brief Compute the Pfaffian of an even-order alternating matrix over a
/// field.  Each step moves a nonzero entry into the next 2-by-2 pivot block;
/// the skew-symmetric Schur complement removes that pair, and the product of
/// pivot entries is the Pfaffian.  Swapping a paired index changes its sign.
template <class T> T pfaffian(std::vector<std::vector<T>> mat) {
  int sz = int(mat.size());
  assert(sz % 2 == 0);
  for (const auto &row : mat) {
    assert(int(row.size()) == sz);
  }
  T res = T(1);
  for (int a = 0; a < sz; a += 2) {
    int piv = a + 1;
    while (piv < sz && mat[a][piv] == T{}) {
      piv++;
    }
    if (piv == sz) {
      return T{};
    }
    if (piv != a + 1) {
      std::swap(mat[piv], mat[a + 1]);
      for (auto &row : mat) {
        std::swap(row[piv], row[a + 1]);
      }
      res = -res;
    }
    T pv = mat[a][a + 1];
    res *= pv;
    T inv = T(1) / pv;
    for (int row = a + 2; row < sz; row++) {
      for (int col = row + 1; col < sz; col++) {
        mat[row][col] -=
            (mat[a][row] * mat[a + 1][col] - mat[a][col] * mat[a + 1][row]) *
            inv;
        mat[col][row] = -mat[row][col];
      }
    }
  }
  return res;
}

} // namespace noya
#ifndef NOYA_PFAFFIAN_HPP
#define NOYA_PFAFFIAN_HPP 1

/// @complexity Time: O(n^3).
/// Space: O(n^2).

#include <algorithm>
#include <cassert>
#include <vector>

namespace noya {

/// @brief Compute the Pfaffian of an even-order alternating matrix over a
/// field.  Each step moves a nonzero entry into the next 2-by-2 pivot block;
/// the skew-symmetric Schur complement removes that pair, and the product of
/// pivot entries is the Pfaffian.  Swapping a paired index changes its sign.
template <class T> T pfaffian(std::vector<std::vector<T>> mat) {
  int sz = int(mat.size());
  assert(sz % 2 == 0);
  for (const auto &row : mat) {
    assert(int(row.size()) == sz);
  }
  T res = T(1);
  for (int a = 0; a < sz; a += 2) {
    int piv = a + 1;
    while (piv < sz && mat[a][piv] == T{}) {
      piv++;
    }
    if (piv == sz) {
      return T{};
    }
    if (piv != a + 1) {
      std::swap(mat[piv], mat[a + 1]);
      for (auto &row : mat) {
        std::swap(row[piv], row[a + 1]);
      }
      res = -res;
    }
    T pv = mat[a][a + 1];
    res *= pv;
    T inv = T(1) / pv;
    for (int row = a + 2; row < sz; row++) {
      for (int col = row + 1; col < sz; col++) {
        mat[row][col] -=
            (mat[a][row] * mat[a + 1][col] - mat[a][col] * mat[a + 1][row]) *
            inv;
        mat[col][row] = -mat[row][col];
      }
    }
  }
  return res;
}

} // namespace noya

#endif // NOYA_PFAFFIAN_HPP
#include <algorithm>
#include <cassert>
#include <vector>

/// @complexity Time: O(n^3).
/// Space: O(n^2).

namespace noya {

/// @brief Compute the Pfaffian of an even-order alternating matrix over a
/// field.  Each step moves a nonzero entry into the next 2-by-2 pivot block;
/// the skew-symmetric Schur complement removes that pair, and the product of
/// pivot entries is the Pfaffian.  Swapping a paired index changes its sign.
template <class T> T pfaffian(std::vector<std::vector<T>> mat) {
  int sz = int(mat.size());
  assert(sz % 2 == 0);
  for (const auto &row : mat) {
    assert(int(row.size()) == sz);
  }
  T res = T(1);
  for (int a = 0; a < sz; a += 2) {
    int piv = a + 1;
    while (piv < sz && mat[a][piv] == T{}) {
      piv++;
    }
    if (piv == sz) {
      return T{};
    }
    if (piv != a + 1) {
      std::swap(mat[piv], mat[a + 1]);
      for (auto &row : mat) {
        std::swap(row[piv], row[a + 1]);
      }
      res = -res;
    }
    T pv = mat[a][a + 1];
    res *= pv;
    T inv = T(1) / pv;
    for (int row = a + 2; row < sz; row++) {
      for (int col = row + 1; col < sz; col++) {
        mat[row][col] -=
            (mat[a][row] * mat[a + 1][col] - mat[a][col] * mat[a + 1][row]) *
            inv;
        mat[col][row] = -mat[row][col];
      }
    }
  }
  return res;
}

} // namespace noya