Skip to content

rollback_dsu.hpp

SECTIONData Structure INCLUDEnoya/rollback_dsu.hpp

支持合并、连通性查询和撤销到历史状态;常配合分治处理离线删边或时间区间事件。

Complexity: Time: O(log n) find/merge and O(1) rollback. Space: O(n + h), where h is the number of retained rollback records.

AC 记录:unionfind

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(log n) find/merge and O(1) rollback.
/// Space: O(n + h), where h is the number of retained rollback records.

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

namespace noya {

/// @brief Union-find with O(log n) queries and O(1) rollback per merge.
struct rollback_dsu {
  struct change {
    int ra;
    int va;
    int rb;
    int vb;
  };

  std::vector<int> p;
  std::vector<change> his;

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

  /// @brief Reset to n singleton components and clear rollback history.
  void build(int n) {
    assert(n >= 0);
    p.assign(n, -1);
    his.clear();
  }

  /// @brief Return the representative of x without path compression.
  int leader(int x) const {
    assert(0 <= x && x < int(p.size()));
    while (p[x] >= 0) {
      x = p[x];
    }
    return x;
  }

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

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

  /// @brief Merge two components and record one rollback step.
  bool merge(int a, int b) {
    a = leader(a);
    b = leader(b);
    if (a == b) {
      his.push_back({-1, 0, -1, 0});
      return false;
    }
    if (-p[a] < -p[b]) {
      std::swap(a, b);
    }
    his.push_back({a, p[a], b, p[b]});
    p[a] += p[b];
    p[b] = a;
    return true;
  }

  /// @brief Return a rollback state for use with rollback().
  int snapshot() const { return int(his.size()); }

  /// @brief Undo the most recent merge attempt.
  void undo() {
    assert(!his.empty());
    change lst = his.back();
    his.pop_back();
    if (lst.ra == -1) {
      return;
    }
    p[lst.ra] = lst.va;
    p[lst.rb] = lst.vb;
  }

  /// @brief Roll back to a value previously returned by snapshot().
  void rollback(int t) {
    assert(0 <= t && t <= int(his.size()));
    while (int(his.size()) > t) {
      undo();
    }
  }
};

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

/// @complexity Time: O(log n) find/merge and O(1) rollback.
/// Space: O(n + h), where h is the number of retained rollback records.

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

namespace noya {

/// @brief Union-find with O(log n) queries and O(1) rollback per merge.
struct rollback_dsu {
  struct change {
    int ra;
    int va;
    int rb;
    int vb;
  };

  std::vector<int> p;
  std::vector<change> his;

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

  /// @brief Reset to n singleton components and clear rollback history.
  void build(int n) {
    assert(n >= 0);
    p.assign(n, -1);
    his.clear();
  }

  /// @brief Return the representative of x without path compression.
  int leader(int x) const {
    assert(0 <= x && x < int(p.size()));
    while (p[x] >= 0) {
      x = p[x];
    }
    return x;
  }

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

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

  /// @brief Merge two components and record one rollback step.
  bool merge(int a, int b) {
    a = leader(a);
    b = leader(b);
    if (a == b) {
      his.push_back({-1, 0, -1, 0});
      return false;
    }
    if (-p[a] < -p[b]) {
      std::swap(a, b);
    }
    his.push_back({a, p[a], b, p[b]});
    p[a] += p[b];
    p[b] = a;
    return true;
  }

  /// @brief Return a rollback state for use with rollback().
  int snapshot() const { return int(his.size()); }

  /// @brief Undo the most recent merge attempt.
  void undo() {
    assert(!his.empty());
    change lst = his.back();
    his.pop_back();
    if (lst.ra == -1) {
      return;
    }
    p[lst.ra] = lst.va;
    p[lst.rb] = lst.vb;
  }

  /// @brief Roll back to a value previously returned by snapshot().
  void rollback(int t) {
    assert(0 <= t && t <= int(his.size()));
    while (int(his.size()) > t) {
      undo();
    }
  }
};

} // namespace noya

#endif // NOYA_ROLLBACK_DSU_HPP
#include <cassert>
#include <utility>
#include <vector>

/// @complexity Time: O(log n) find/merge and O(1) rollback.
/// Space: O(n + h), where h is the number of retained rollback records.

namespace noya {

/// @brief Union-find with O(log n) queries and O(1) rollback per merge.
struct rollback_dsu {
  struct change {
    int ra;
    int va;
    int rb;
    int vb;
  };

  std::vector<int> p;
  std::vector<change> his;

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

  /// @brief Reset to n singleton components and clear rollback history.
  void build(int n) {
    assert(n >= 0);
    p.assign(n, -1);
    his.clear();
  }

  /// @brief Return the representative of x without path compression.
  int leader(int x) const {
    assert(0 <= x && x < int(p.size()));
    while (p[x] >= 0) {
      x = p[x];
    }
    return x;
  }

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

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

  /// @brief Merge two components and record one rollback step.
  bool merge(int a, int b) {
    a = leader(a);
    b = leader(b);
    if (a == b) {
      his.push_back({-1, 0, -1, 0});
      return false;
    }
    if (-p[a] < -p[b]) {
      std::swap(a, b);
    }
    his.push_back({a, p[a], b, p[b]});
    p[a] += p[b];
    p[b] = a;
    return true;
  }

  /// @brief Return a rollback state for use with rollback().
  int snapshot() const { return int(his.size()); }

  /// @brief Undo the most recent merge attempt.
  void undo() {
    assert(!his.empty());
    change lst = his.back();
    his.pop_back();
    if (lst.ra == -1) {
      return;
    }
    p[lst.ra] = lst.va;
    p[lst.rb] = lst.vb;
  }

  /// @brief Roll back to a value previously returned by snapshot().
  void rollback(int t) {
    assert(0 <= t && t <= int(his.size()));
    while (int(his.size()) > t) {
      undo();
    }
  }
};

} // namespace noya