Skip to content

persistent_dsu.hpp

SECTIONData Structure INCLUDEnoya/persistent_dsu.hpp

查询并查集在任意历史版本中的连通关系,并从旧版本继续合并;适合版本树上的动态连通性。

Complexity: Time: O(log^2 n) per leader, same, size, or merge query. Space: O(log n) new nodes per successful merge and O(1) per version copy.

AC 记录:persistent_unionfind

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(log^2 n) per leader, same, size, or merge query.
/// Space: O(log n) new nodes per successful merge and O(1) per version copy.

#include <cassert>
#include <memory>
#include <utility>
#include <vector>

namespace noya {

/// @brief Fully persistent union-find.  Union by size keeps every parent chain
/// logarithmic, while a path-copying segment tree stores the parent-or-size
/// array.  Versions share all untouched segment-tree nodes, so any old version
/// can be queried or branched from after later merges.
class persistent_dsu {
  struct node {
    int ls = 0;
    int rs = 0;
    int val = -1;
  };

  struct storage {
    std::vector<node> tr{{}};

    int get(int rt, int low, int hi, int pos) const {
      if (rt == 0) {
        return -1;
      }
      if (hi - low == 1) {
        return tr[rt].val;
      }
      int mid = low + (hi - low) / 2;
      if (pos < mid) {
        return get(tr[rt].ls, low, mid, pos);
      }
      return get(tr[rt].rs, mid, hi, pos);
    }

    int set(int rt, int low, int hi, int pos, int val) {
      int cur = int(tr.size());
      tr.push_back(tr[rt]);
      if (hi - low == 1) {
        tr[cur].val = val;
        return cur;
      }
      int mid = low + (hi - low) / 2;
      if (pos < mid) {
        tr[cur].ls = set(tr[rt].ls, low, mid, pos, val);
      } else {
        tr[cur].rs = set(tr[rt].rs, mid, hi, pos, val);
      }
      return cur;
    }
  };

  int n = 0;
  int rt0 = 0;
  std::shared_ptr<storage> dat;

  persistent_dsu(int cnt, int rt, std::shared_ptr<storage> da0)
      : n(cnt), rt0(rt), dat(std::move(da0)) {}

  int value(int pos) const {
    assert(dat && 0 <= pos && pos < n);
    return dat->get(rt0, 0, n, pos);
  }

public:
  persistent_dsu() = default;
  explicit persistent_dsu(int cnt) : n(cnt), dat(std::make_shared<storage>()) {
    assert(cnt > 0);
  }

  /// @brief Return the representative of vertex in this version.
  int leader(int u) const {
    int fa = value(u);
    while (fa >= 0) {
      u = fa;
      fa = value(u);
    }
    return u;
  }

  bool same(int a, int b) const { return leader(a) == leader(b); }

  int size(int u) const { return -value(leader(u)); }

  /// @brief Return a new version in which the two components are united.
  persistent_dsu merge(int a, int b) const {
    a = leader(a);
    b = leader(b);
    if (a == b) {
      return *this;
    }
    int sz1 = -value(a);
    int sz2 = -value(b);
    if (sz1 < sz2) {
      std::swap(a, b);
      std::swap(sz1, sz2);
    }
    int nr = dat->set(rt0, 0, n, a, -(sz1 + sz2));
    nr = dat->set(nr, 0, n, b, a);
    return persistent_dsu(n, nr, dat);
  }
};

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

/// @complexity Time: O(log^2 n) per leader, same, size, or merge query.
/// Space: O(log n) new nodes per successful merge and O(1) per version copy.

#include <cassert>
#include <memory>
#include <utility>
#include <vector>

namespace noya {

/// @brief Fully persistent union-find.  Union by size keeps every parent chain
/// logarithmic, while a path-copying segment tree stores the parent-or-size
/// array.  Versions share all untouched segment-tree nodes, so any old version
/// can be queried or branched from after later merges.
class persistent_dsu {
  struct node {
    int ls = 0;
    int rs = 0;
    int val = -1;
  };

  struct storage {
    std::vector<node> tr{{}};

    int get(int rt, int low, int hi, int pos) const {
      if (rt == 0) {
        return -1;
      }
      if (hi - low == 1) {
        return tr[rt].val;
      }
      int mid = low + (hi - low) / 2;
      if (pos < mid) {
        return get(tr[rt].ls, low, mid, pos);
      }
      return get(tr[rt].rs, mid, hi, pos);
    }

    int set(int rt, int low, int hi, int pos, int val) {
      int cur = int(tr.size());
      tr.push_back(tr[rt]);
      if (hi - low == 1) {
        tr[cur].val = val;
        return cur;
      }
      int mid = low + (hi - low) / 2;
      if (pos < mid) {
        tr[cur].ls = set(tr[rt].ls, low, mid, pos, val);
      } else {
        tr[cur].rs = set(tr[rt].rs, mid, hi, pos, val);
      }
      return cur;
    }
  };

  int n = 0;
  int rt0 = 0;
  std::shared_ptr<storage> dat;

  persistent_dsu(int cnt, int rt, std::shared_ptr<storage> da0)
      : n(cnt), rt0(rt), dat(std::move(da0)) {}

  int value(int pos) const {
    assert(dat && 0 <= pos && pos < n);
    return dat->get(rt0, 0, n, pos);
  }

public:
  persistent_dsu() = default;
  explicit persistent_dsu(int cnt) : n(cnt), dat(std::make_shared<storage>()) {
    assert(cnt > 0);
  }

  /// @brief Return the representative of vertex in this version.
  int leader(int u) const {
    int fa = value(u);
    while (fa >= 0) {
      u = fa;
      fa = value(u);
    }
    return u;
  }

  bool same(int a, int b) const { return leader(a) == leader(b); }

  int size(int u) const { return -value(leader(u)); }

  /// @brief Return a new version in which the two components are united.
  persistent_dsu merge(int a, int b) const {
    a = leader(a);
    b = leader(b);
    if (a == b) {
      return *this;
    }
    int sz1 = -value(a);
    int sz2 = -value(b);
    if (sz1 < sz2) {
      std::swap(a, b);
      std::swap(sz1, sz2);
    }
    int nr = dat->set(rt0, 0, n, a, -(sz1 + sz2));
    nr = dat->set(nr, 0, n, b, a);
    return persistent_dsu(n, nr, dat);
  }
};

} // namespace noya

#endif // NOYA_PERSISTENT_DSU_HPP
#include <cassert>
#include <memory>
#include <utility>
#include <vector>

/// @complexity Time: O(log^2 n) per leader, same, size, or merge query.
/// Space: O(log n) new nodes per successful merge and O(1) per version copy.

namespace noya {

/// @brief Fully persistent union-find.  Union by size keeps every parent chain
/// logarithmic, while a path-copying segment tree stores the parent-or-size
/// array.  Versions share all untouched segment-tree nodes, so any old version
/// can be queried or branched from after later merges.
class persistent_dsu {
  struct node {
    int ls = 0;
    int rs = 0;
    int val = -1;
  };

  struct storage {
    std::vector<node> tr{{}};

    int get(int rt, int low, int hi, int pos) const {
      if (rt == 0) {
        return -1;
      }
      if (hi - low == 1) {
        return tr[rt].val;
      }
      int mid = low + (hi - low) / 2;
      if (pos < mid) {
        return get(tr[rt].ls, low, mid, pos);
      }
      return get(tr[rt].rs, mid, hi, pos);
    }

    int set(int rt, int low, int hi, int pos, int val) {
      int cur = int(tr.size());
      tr.push_back(tr[rt]);
      if (hi - low == 1) {
        tr[cur].val = val;
        return cur;
      }
      int mid = low + (hi - low) / 2;
      if (pos < mid) {
        tr[cur].ls = set(tr[rt].ls, low, mid, pos, val);
      } else {
        tr[cur].rs = set(tr[rt].rs, mid, hi, pos, val);
      }
      return cur;
    }
  };

  int n = 0;
  int rt0 = 0;
  std::shared_ptr<storage> dat;

  persistent_dsu(int cnt, int rt, std::shared_ptr<storage> da0)
      : n(cnt), rt0(rt), dat(std::move(da0)) {}

  int value(int pos) const {
    assert(dat && 0 <= pos && pos < n);
    return dat->get(rt0, 0, n, pos);
  }

public:
  persistent_dsu() = default;
  explicit persistent_dsu(int cnt) : n(cnt), dat(std::make_shared<storage>()) {
    assert(cnt > 0);
  }

  /// @brief Return the representative of vertex in this version.
  int leader(int u) const {
    int fa = value(u);
    while (fa >= 0) {
      u = fa;
      fa = value(u);
    }
    return u;
  }

  bool same(int a, int b) const { return leader(a) == leader(b); }

  int size(int u) const { return -value(leader(u)); }

  /// @brief Return a new version in which the two components are united.
  persistent_dsu merge(int a, int b) const {
    a = leader(a);
    b = leader(b);
    if (a == b) {
      return *this;
    }
    int sz1 = -value(a);
    int sz2 = -value(b);
    if (sz1 < sz2) {
      std::swap(a, b);
      std::swap(sz1, sz2);
    }
    int nr = dat->set(rt0, 0, n, a, -(sz1 + sz2));
    nr = dat->set(nr, 0, n, b, a);
    return persistent_dsu(n, nr, dat);
  }
};

} // namespace noya