Skip to the content.

:warning: Persistent Potentialized Union Find
(union-find/persistent-potentialized-union-find.hpp)

永続ポテンシャル付き Union Find.

通常の Union Find の連結性に加えて,各頂点 $v$ に値 $p_v$ があるとして差分の情報を管理する. 各状態を snapshot として保存し,あとから過去の状態に戻せる.

PersistentPotentializedUnionFind<G> として使う. Gvalue_type, op(x,y), e(), inv(x) を持つ群を表す型. 加法の場合は AddGroup<T> を使えばよい.

永続化には PersistentArray を用いているため,path compression は行わない. 各操作の時間計算量は木の高さと永続配列の更新に依存する.

Depends on

Code

#pragma once

#include "data-structure/persistent-array.hpp"
#include "algebraic-structure/group.hpp"

template <class G>
REQUIRES(Group<G>)
struct PersistentPotentializedUnionFind {
  using T = typename G::value_type;

 private:
  PersistentArray<int, 8> a;
  PersistentArray<T, 8> p;

 public:
  PersistentPotentializedUnionFind(int n) {
    a.build(vector<int>(n, -1));
    p.build(vector<T>(n, G::e()));
  }
  int find(int x) { return a.get(x) < 0 ? x : find(a.get(x)); }
  T pot(int x) {
    int y = a.get(x);
    return y < 0 ? p.get(x) : G::op(p.get(x), pot(y));
  }
  T diff(int x, int y) { return G::op(pot(x), G::inv(pot(y))); }
  int size(int x) { return -a.get(find(x)); }
  bool same(int x, int y) { return find(x) == find(y); }
  // pot(u) - pot(v) = w
  // return : consistent
  bool unite(int u, int v, T w) {
    int x = find(u), y = find(v);
    if (x == y) return diff(u, v) == w;
    w = G::op(G::op(w, G::inv(pot(u))), pot(v));
    auto ax = a.get(x);
    auto ay = a.get(y);
    if (ax < ay) {
      *p.mutable_get(y) = G::op(p.get(x), G::inv(w));
      *a.mutable_get(x) += ay;
      *a.mutable_get(y) = x;
    } else {
      *p.mutable_get(x) = G::op(p.get(y), w);
      *a.mutable_get(y) += ax;
      *a.mutable_get(x) = y;
    }
    return true;
  }
  int take_snapshot() {
    a.take_snapshot();
    return p.take_snapshot();
  }
  void apply_snapshot(int k) {
    a.apply_snapshot(k);
    p.apply_snapshot(k);
  }
};
/**
 * @brief Persistent Potentialized Union Find
 * @docs docs/union-find/persistent-potentialized-union-find.md
 */
#line 2 "union-find/persistent-potentialized-union-find.hpp"

#line 2 "data-structure/persistent-array.hpp"

template <class T, int B = 8>
struct PersistentArray {
  struct Node {
    T val;
    Node* child[B] = {};
    Node() {}
    Node(const T& v) : val(v) {}
  };
  Node* root;
  vector<Node*> snapshots;
  PersistentArray() : root(nullptr) {}
  T get(Node* t, int k) { return k == 0 ? t->val : get(t->child[k % B], k / B); }
  T get(const int& k) { return get(root, k); }
  pair<Node*, T*> mutable_get(Node* t, int k) {
    t = t ? new Node(*t) : new Node();
    if (k == 0) return {t, &t->val};
    auto p = mutable_get(t->child[k % B], k / B);
    t->child[k % B] = p.first;
    return {t, p.second};
  }
  T* mutable_get(const int& k) {
    auto ret = mutable_get(root, k);
    root = ret.first;
    return ret.second;
  }
  Node* build(Node* t, const T& val, int k) {
    if (!t) t = new Node();
    if (k == 0) {
      t->val = val;
      return t;
    }
    t->child[k % B] = build(t->child[k % B], val, k / B);
    return t;
  }
  void build(const vector<T>& v) {
    root = nullptr;
    for (int i = 0; i < v.size(); i++) root = build(root, v[i], i);
  }
  int take_snapshot() {
    snapshots.push_back(root);
    return snapshots.size() - 1;
  }
  void apply_snapshot(int k) {
    root = snapshots[k];
  }
};

/**
 * @brief Persistent Array
 * @docs docs/val-structure/persistent-array.md
 */
#line 2 "algebraic-structure/util.hpp"
#ifdef __cpp_concepts
#define REQUIRES(...) requires __VA_ARGS__
#else
#define REQUIRES(...)
#endif
#line 3 "algebraic-structure/magma.hpp"

#ifdef __cpp_concepts
template <class M>
concept Magma = requires(typename M::value_type x, typename M::value_type y) {
  typename M::value_type;
  { M::op(x, y) } -> same_as<typename M::value_type>;
};
#endif

template <class T>
struct AddMagma {
  using value_type = T;
  static T op(T x, T y) { return x + y; }
};
template <class T>
struct MulMagma {
  using value_type = T;
  static T op(T x, T y) { return x * y; }
};
template <class T, T id>
struct MaxMagma {
  using value_type = T;
  static T op(T x, T y) { return x > y ? x : y; }
};
template <class T, T id>
struct MinMagma {
  using value_type = T;
  static T op(T x, T y) { return x < y ? x : y; }
};
#line 3 "algebraic-structure/monoid.hpp"

#ifdef __cpp_concepts
template <class M>
concept Monoid = Magma<M> && requires {
  { M::e() } -> same_as<typename M::value_type>;
};
#endif

template <class T>
struct AddMonoid {
  using value_type = T;
  static T op(T x, T y) { return x + y; }
  static T e() { return T(0); }
};
template <class T>
struct MulMonoid {
  using value_type = T;
  static T op(T x, T y) { return x * y; }
  static T e() { return T(1); }
};
template <class T, T id>
struct MaxMonoid {
  using value_type = T;
  static T op(T x, T y) { return x > y ? x : y; }
  static T e() { return id; }
};
template <class T, T id>
struct MinMonoid {
  using value_type = T;
  static T op(T x, T y) { return x < y ? x : y; }
  static T e() { return id; }
};
#line 3 "algebraic-structure/group.hpp"

#ifdef __cpp_concepts
template <class G>
concept Group = Monoid<G> && requires(typename G::value_type x) {
  { G::inv(x) } -> same_as<typename G::value_type>;
};
#endif

template <class T>
struct AddGroup {
  using value_type = T;
  static T op(T x, T y) { return x + y; }
  static T e() { return T(0); }
  static T inv(T x) { return -x; }
};
template <class T>
struct MulGroup {
  using value_type = T;
  static T op(T x, T y) { return x * y; }
  static T e() { return T(1); }
  static T inv(T x) { return T(1) / x; }
};
#line 5 "union-find/persistent-potentialized-union-find.hpp"

template <class G>
REQUIRES(Group<G>)
struct PersistentPotentializedUnionFind {
  using T = typename G::value_type;

 private:
  PersistentArray<int, 8> a;
  PersistentArray<T, 8> p;

 public:
  PersistentPotentializedUnionFind(int n) {
    a.build(vector<int>(n, -1));
    p.build(vector<T>(n, G::e()));
  }
  int find(int x) { return a.get(x) < 0 ? x : find(a.get(x)); }
  T pot(int x) {
    int y = a.get(x);
    return y < 0 ? p.get(x) : G::op(p.get(x), pot(y));
  }
  T diff(int x, int y) { return G::op(pot(x), G::inv(pot(y))); }
  int size(int x) { return -a.get(find(x)); }
  bool same(int x, int y) { return find(x) == find(y); }
  // pot(u) - pot(v) = w
  // return : consistent
  bool unite(int u, int v, T w) {
    int x = find(u), y = find(v);
    if (x == y) return diff(u, v) == w;
    w = G::op(G::op(w, G::inv(pot(u))), pot(v));
    auto ax = a.get(x);
    auto ay = a.get(y);
    if (ax < ay) {
      *p.mutable_get(y) = G::op(p.get(x), G::inv(w));
      *a.mutable_get(x) += ay;
      *a.mutable_get(y) = x;
    } else {
      *p.mutable_get(x) = G::op(p.get(y), w);
      *a.mutable_get(y) += ax;
      *a.mutable_get(x) = y;
    }
    return true;
  }
  int take_snapshot() {
    a.take_snapshot();
    return p.take_snapshot();
  }
  void apply_snapshot(int k) {
    a.apply_snapshot(k);
    p.apply_snapshot(k);
  }
};
/**
 * @brief Persistent Potentialized Union Find
 * @docs docs/union-find/persistent-potentialized-union-find.md
 */
Back to top page