Skip to content

mo.hpp

SECTIONData Structure INCLUDEnoya/mo.hpp

把静态区间询问重排,使左右端点移动总量较小;适合能在增删一个元素时维护答案的离线查询。

Complexity: Time: O((N + Q) sqrt(N)) add/remove steps with standard block ordering. Space: O(Q).

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O((N + Q) sqrt(N)) add/remove steps with standard block ordering.
/// Space: O(Q).

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

namespace noya {

/// @brief Mo's algorithm for offline half-open range queries.
struct mo {
  int n = 0;
  std::vector<std::pair<int, int>> qs;

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

  /// @brief Append [l, r) and return its query id.
  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;
  }

  /// @brief Move one shared window through all queries and call answer(id)
  /// after the corresponding range is active.
  template <class AddLeft, class AddRight, class RemoveLeft, class RemoveRight,
            class Answer>
  void run(AddLeft al, AddRight ar, RemoveLeft dl, RemoveRight dr,
           Answer ans) const {
    int q = int(qs.size());
    if (q == 0) {
      return;
    }
    int bs = std::max(1, int(n / std::max(1.0, std::sqrt(double(q)))));
    std::vector<int> ord(q);
    std::iota(ord.begin(), ord.end(), 0);
    std::sort(ord.begin(), ord.end(), [&](int a, int b) {
      int b1 = qs[a].first / bs;
      int b2 = qs[b].first / bs;
      if (b1 != b2) {
        return b1 < b2;
      }
      if (b1 & 1) {
        return qs[a].second > qs[b].second;
      }
      return qs[a].second < qs[b].second;
    });

    int l = 0;
    int r = 0;
    for (int id : ord) {
      auto [ql, qr] = qs[id];
      while (ql < l) {
        al(--l);
      }
      while (r < qr) {
        ar(r++);
      }
      while (l < ql) {
        dl(l++);
      }
      while (qr < r) {
        dr(--r);
      }
      ans(id);
    }
  }
};

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

/// @complexity Time: O((N + Q) sqrt(N)) add/remove steps with standard block ordering.
/// Space: O(Q).

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

namespace noya {

/// @brief Mo's algorithm for offline half-open range queries.
struct mo {
  int n = 0;
  std::vector<std::pair<int, int>> qs;

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

  /// @brief Append [l, r) and return its query id.
  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;
  }

  /// @brief Move one shared window through all queries and call answer(id)
  /// after the corresponding range is active.
  template <class AddLeft, class AddRight, class RemoveLeft, class RemoveRight,
            class Answer>
  void run(AddLeft al, AddRight ar, RemoveLeft dl, RemoveRight dr,
           Answer ans) const {
    int q = int(qs.size());
    if (q == 0) {
      return;
    }
    int bs = std::max(1, int(n / std::max(1.0, std::sqrt(double(q)))));
    std::vector<int> ord(q);
    std::iota(ord.begin(), ord.end(), 0);
    std::sort(ord.begin(), ord.end(), [&](int a, int b) {
      int b1 = qs[a].first / bs;
      int b2 = qs[b].first / bs;
      if (b1 != b2) {
        return b1 < b2;
      }
      if (b1 & 1) {
        return qs[a].second > qs[b].second;
      }
      return qs[a].second < qs[b].second;
    });

    int l = 0;
    int r = 0;
    for (int id : ord) {
      auto [ql, qr] = qs[id];
      while (ql < l) {
        al(--l);
      }
      while (r < qr) {
        ar(r++);
      }
      while (l < ql) {
        dl(l++);
      }
      while (qr < r) {
        dr(--r);
      }
      ans(id);
    }
  }
};

} // namespace noya

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

/// @complexity Time: O((N + Q) sqrt(N)) add/remove steps with standard block ordering.
/// Space: O(Q).

namespace noya {

/// @brief Mo's algorithm for offline half-open range queries.
struct mo {
  int n = 0;
  std::vector<std::pair<int, int>> qs;

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

  /// @brief Append [l, r) and return its query id.
  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;
  }

  /// @brief Move one shared window through all queries and call answer(id)
  /// after the corresponding range is active.
  template <class AddLeft, class AddRight, class RemoveLeft, class RemoveRight,
            class Answer>
  void run(AddLeft al, AddRight ar, RemoveLeft dl, RemoveRight dr,
           Answer ans) const {
    int q = int(qs.size());
    if (q == 0) {
      return;
    }
    int bs = std::max(1, int(n / std::max(1.0, std::sqrt(double(q)))));
    std::vector<int> ord(q);
    std::iota(ord.begin(), ord.end(), 0);
    std::sort(ord.begin(), ord.end(), [&](int a, int b) {
      int b1 = qs[a].first / bs;
      int b2 = qs[b].first / bs;
      if (b1 != b2) {
        return b1 < b2;
      }
      if (b1 & 1) {
        return qs[a].second > qs[b].second;
      }
      return qs[a].second < qs[b].second;
    });

    int l = 0;
    int r = 0;
    for (int id : ord) {
      auto [ql, qr] = qs[id];
      while (ql < l) {
        al(--l);
      }
      while (r < qr) {
        ar(r++);
      }
      while (l < ql) {
        dl(l++);
      }
      while (qr < r) {
        dr(--r);
      }
      ans(id);
    }
  }
};

} // namespace noya