Skip to content

rollback_mo.hpp

SECTIONData Structure INCLUDEnoya/rollback_mo.hpp

以回滚结构处理离线区间询问,避免删除操作;适合只能添加元素、但状态可以整体撤销的答案维护。

Complexity: Time: O((N + Q) sqrt(N)) add/rollback work with standard blocking. Space: O(N + Q).

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O((N + Q) sqrt(N)) add/rollback work with standard blocking.
/// Space: O(N + Q).

#include <algorithm>
#include <cassert>
#include <cmath>
#include <numeric>
#include <utility>
#include <vector>

namespace noya {

/// @brief Offline half-open range scheduler for rollbackable add-only states.
/// Each query uses add(position), snapshot(), rollback(token), and answer(id).
struct rollback_mo {
  int n = 0;
  std::vector<std::pair<int, int>> qs;

  rollback_mo() = default;
  explicit rollback_mo(int n_) : n(n_) { assert(n >= 0); }

  int add_query(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    int id = int(qs.size());
    qs.emplace_back(l, r);
    return id;
  }

  template <class Add, class Snapshot, class Rollback, class Answer>
  void run(Add add, Snapshot ss, Rollback rb, Answer ans) const {
    if (qs.empty()) {
      return;
    }
    int bs = std::max(1, int(std::sqrt(std::max(1, n))));
    int nb = (n + bs - 1) / bs + 1;
    std::vector<std::vector<int>> gs(nb);
    auto a0 = ss();

    for (int id = 0; id < int(qs.size()); id++) {
      auto [l, r] = qs[id];
      int blk = l / bs;
      int br = std::min(n, (blk + 1) * bs);
      if (r <= br) {
        auto tok = ss();
        for (int pos = l; pos < r; pos++) {
          add(pos);
        }
        ans(id);
        rb(tok);
      } else {
        gs[blk].push_back(id);
      }
    }

    for (int blk = 0; blk < nb; blk++) {
      auto &ord = gs[blk];
      if (ord.empty()) {
        continue;
      }
      std::sort(ord.begin(), ord.end(), [&](int a, int b) {
        return qs[a].second < qs[b].second;
      });
      int br = std::min(n, (blk + 1) * bs);
      int r = br;
      for (int id : ord) {
        auto [ql, qr] = qs[id];
        while (r < qr) {
          add(r++);
        }
        auto tok = ss();
        for (int pos = br - 1; pos >= ql; pos--) {
          add(pos);
        }
        ans(id);
        rb(tok);
      }
      rb(a0);
    }
  }
};

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

/// @complexity Time: O((N + Q) sqrt(N)) add/rollback work with standard blocking.
/// Space: O(N + Q).

#include <algorithm>
#include <cassert>
#include <cmath>
#include <numeric>
#include <utility>
#include <vector>

namespace noya {

/// @brief Offline half-open range scheduler for rollbackable add-only states.
/// Each query uses add(position), snapshot(), rollback(token), and answer(id).
struct rollback_mo {
  int n = 0;
  std::vector<std::pair<int, int>> qs;

  rollback_mo() = default;
  explicit rollback_mo(int n_) : n(n_) { assert(n >= 0); }

  int add_query(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    int id = int(qs.size());
    qs.emplace_back(l, r);
    return id;
  }

  template <class Add, class Snapshot, class Rollback, class Answer>
  void run(Add add, Snapshot ss, Rollback rb, Answer ans) const {
    if (qs.empty()) {
      return;
    }
    int bs = std::max(1, int(std::sqrt(std::max(1, n))));
    int nb = (n + bs - 1) / bs + 1;
    std::vector<std::vector<int>> gs(nb);
    auto a0 = ss();

    for (int id = 0; id < int(qs.size()); id++) {
      auto [l, r] = qs[id];
      int blk = l / bs;
      int br = std::min(n, (blk + 1) * bs);
      if (r <= br) {
        auto tok = ss();
        for (int pos = l; pos < r; pos++) {
          add(pos);
        }
        ans(id);
        rb(tok);
      } else {
        gs[blk].push_back(id);
      }
    }

    for (int blk = 0; blk < nb; blk++) {
      auto &ord = gs[blk];
      if (ord.empty()) {
        continue;
      }
      std::sort(ord.begin(), ord.end(), [&](int a, int b) {
        return qs[a].second < qs[b].second;
      });
      int br = std::min(n, (blk + 1) * bs);
      int r = br;
      for (int id : ord) {
        auto [ql, qr] = qs[id];
        while (r < qr) {
          add(r++);
        }
        auto tok = ss();
        for (int pos = br - 1; pos >= ql; pos--) {
          add(pos);
        }
        ans(id);
        rb(tok);
      }
      rb(a0);
    }
  }
};

} // namespace noya

#endif // NOYA_ROLLBACK_MO_HPP
#include <algorithm>
#include <cassert>
#include <cmath>
#include <numeric>
#include <utility>
#include <vector>

/// @complexity Time: O((N + Q) sqrt(N)) add/rollback work with standard blocking.
/// Space: O(N + Q).

namespace noya {

/// @brief Offline half-open range scheduler for rollbackable add-only states.
/// Each query uses add(position), snapshot(), rollback(token), and answer(id).
struct rollback_mo {
  int n = 0;
  std::vector<std::pair<int, int>> qs;

  rollback_mo() = default;
  explicit rollback_mo(int n_) : n(n_) { assert(n >= 0); }

  int add_query(int l, int r) {
    assert(0 <= l && l <= r && r <= n);
    int id = int(qs.size());
    qs.emplace_back(l, r);
    return id;
  }

  template <class Add, class Snapshot, class Rollback, class Answer>
  void run(Add add, Snapshot ss, Rollback rb, Answer ans) const {
    if (qs.empty()) {
      return;
    }
    int bs = std::max(1, int(std::sqrt(std::max(1, n))));
    int nb = (n + bs - 1) / bs + 1;
    std::vector<std::vector<int>> gs(nb);
    auto a0 = ss();

    for (int id = 0; id < int(qs.size()); id++) {
      auto [l, r] = qs[id];
      int blk = l / bs;
      int br = std::min(n, (blk + 1) * bs);
      if (r <= br) {
        auto tok = ss();
        for (int pos = l; pos < r; pos++) {
          add(pos);
        }
        ans(id);
        rb(tok);
      } else {
        gs[blk].push_back(id);
      }
    }

    for (int blk = 0; blk < nb; blk++) {
      auto &ord = gs[blk];
      if (ord.empty()) {
        continue;
      }
      std::sort(ord.begin(), ord.end(), [&](int a, int b) {
        return qs[a].second < qs[b].second;
      });
      int br = std::min(n, (blk + 1) * bs);
      int r = br;
      for (int id : ord) {
        auto [ql, qr] = qs[id];
        while (r < qr) {
          add(r++);
        }
        auto tok = ss();
        for (int pos = br - 1; pos >= ql; pos--) {
          add(pos);
        }
        ans(id);
        rb(tok);
      }
      rb(a0);
    }
  }
};

} // namespace noya