Skip to content

potentialized_dsu.hpp

SECTIONData Structure INCLUDEnoya/potentialized_dsu.hpp

维护带势能差的并查集,支持加入两点差值约束并查询相对势能;适合加权并查集模型。

Complexity: Time: Amortized O(alpha(n)) find/merge/difference. Space: O(n).

AC 记录:unionfind_with_potential

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: Amortized O(alpha(n)) find/merge/difference.
/// Space: O(n).

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

namespace noya {

/// @brief Weighted union-find over an additive group.
/// merge(a, b, w) enforces potential(b) - potential(a) = w.
template <class T> struct potentialized_dsu {
  std::vector<int> p;
  std::vector<T> w;

  potentialized_dsu() = default;
  explicit potentialized_dsu(int n) { build(n); }

  /// @brief Reset to n singleton components with zero potentials.
  void build(int n) {
    assert(n >= 0);
    p.assign(n, -1);
    w.assign(n, T{});
  }

  /// @brief Return the representative of x with path compression.
  int leader(int x) {
    assert(0 <= x && x < int(p.size()));
    if (p[x] < 0) {
      return x;
    }
    int fa = p[x];
    int rt = leader(fa);
    w[x] += w[fa];
    return p[x] = rt;
  }

  /// @brief Return potential(x) relative to its component representative.
  T potential(int x) {
    leader(x);
    return w[x];
  }

  /// @brief Return whether a and b are in the same component.
  bool same(int a, int b) { return leader(a) == leader(b); }

  /// @brief Return the size of the component containing x.
  int size(int x) { return -p[leader(x)]; }

  /// @brief Return potential(b) - potential(a), or nullopt if disconnected.
  std::optional<T> difference(int a, int b) {
    if (!same(a, b)) {
      return std::nullopt;
    }
    return potential(b) - potential(a);
  }

  /// @brief Add a potential constraint; return false only if it contradicts
  /// existing constraints.
  bool merge(int a, int b, T dif) {
    T wa = potential(a);
    T wb = potential(b);
    int ra = leader(a);
    int rb = leader(b);
    if (ra == rb) {
      return wb - wa == dif;
    }

    T dw = dif + wa - wb;
    if (-p[ra] < -p[rb]) {
      std::swap(ra, rb);
      dw = -dw;
    }
    p[ra] += p[rb];
    p[rb] = ra;
    w[rb] = dw;
    return true;
  }
};

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

/// @complexity Time: Amortized O(alpha(n)) find/merge/difference.
/// Space: O(n).

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

namespace noya {

/// @brief Weighted union-find over an additive group.
/// merge(a, b, w) enforces potential(b) - potential(a) = w.
template <class T> struct potentialized_dsu {
  std::vector<int> p;
  std::vector<T> w;

  potentialized_dsu() = default;
  explicit potentialized_dsu(int n) { build(n); }

  /// @brief Reset to n singleton components with zero potentials.
  void build(int n) {
    assert(n >= 0);
    p.assign(n, -1);
    w.assign(n, T{});
  }

  /// @brief Return the representative of x with path compression.
  int leader(int x) {
    assert(0 <= x && x < int(p.size()));
    if (p[x] < 0) {
      return x;
    }
    int fa = p[x];
    int rt = leader(fa);
    w[x] += w[fa];
    return p[x] = rt;
  }

  /// @brief Return potential(x) relative to its component representative.
  T potential(int x) {
    leader(x);
    return w[x];
  }

  /// @brief Return whether a and b are in the same component.
  bool same(int a, int b) { return leader(a) == leader(b); }

  /// @brief Return the size of the component containing x.
  int size(int x) { return -p[leader(x)]; }

  /// @brief Return potential(b) - potential(a), or nullopt if disconnected.
  std::optional<T> difference(int a, int b) {
    if (!same(a, b)) {
      return std::nullopt;
    }
    return potential(b) - potential(a);
  }

  /// @brief Add a potential constraint; return false only if it contradicts
  /// existing constraints.
  bool merge(int a, int b, T dif) {
    T wa = potential(a);
    T wb = potential(b);
    int ra = leader(a);
    int rb = leader(b);
    if (ra == rb) {
      return wb - wa == dif;
    }

    T dw = dif + wa - wb;
    if (-p[ra] < -p[rb]) {
      std::swap(ra, rb);
      dw = -dw;
    }
    p[ra] += p[rb];
    p[rb] = ra;
    w[rb] = dw;
    return true;
  }
};

} // namespace noya

#endif // NOYA_POTENTIALIZED_DSU_HPP
#include <cassert>
#include <optional>
#include <utility>
#include <vector>

/// @complexity Time: Amortized O(alpha(n)) find/merge/difference.
/// Space: O(n).

namespace noya {

/// @brief Weighted union-find over an additive group.
/// merge(a, b, w) enforces potential(b) - potential(a) = w.
template <class T> struct potentialized_dsu {
  std::vector<int> p;
  std::vector<T> w;

  potentialized_dsu() = default;
  explicit potentialized_dsu(int n) { build(n); }

  /// @brief Reset to n singleton components with zero potentials.
  void build(int n) {
    assert(n >= 0);
    p.assign(n, -1);
    w.assign(n, T{});
  }

  /// @brief Return the representative of x with path compression.
  int leader(int x) {
    assert(0 <= x && x < int(p.size()));
    if (p[x] < 0) {
      return x;
    }
    int fa = p[x];
    int rt = leader(fa);
    w[x] += w[fa];
    return p[x] = rt;
  }

  /// @brief Return potential(x) relative to its component representative.
  T potential(int x) {
    leader(x);
    return w[x];
  }

  /// @brief Return whether a and b are in the same component.
  bool same(int a, int b) { return leader(a) == leader(b); }

  /// @brief Return the size of the component containing x.
  int size(int x) { return -p[leader(x)]; }

  /// @brief Return potential(b) - potential(a), or nullopt if disconnected.
  std::optional<T> difference(int a, int b) {
    if (!same(a, b)) {
      return std::nullopt;
    }
    return potential(b) - potential(a);
  }

  /// @brief Add a potential constraint; return false only if it contradicts
  /// existing constraints.
  bool merge(int a, int b, T dif) {
    T wa = potential(a);
    T wb = potential(b);
    int ra = leader(a);
    int rb = leader(b);
    if (ra == rb) {
      return wb - wa == dif;
    }

    T dw = dif + wa - wb;
    if (-p[ra] < -p[rb]) {
      std::swap(ra, rb);
      dw = -dw;
    }
    p[ra] += p[rb];
    p[rb] = ra;
    w[rb] = dw;
    return true;
  }
};

} // namespace noya