Skip to content

parallel_binary_search.hpp

SECTIONOptimization INCLUDEnoya/parallel_binary_search.hpp

Run parallel binary search over prefixes of a common update list. ok must be a known true boundary and ng a known false boundary. Calling check(q) after t calls to update represents the predicate at prefix t.

把多组答案同时在同一串更新前缀上二分;适合判定随时间单调、可批量重放更新的离线题。

Implementation

View on GitHub

#ifndef NOYA_PARALLEL_BINARY_SEARCH_HPP
#define NOYA_PARALLEL_BINARY_SEARCH_HPP 1

/// @complexity Time: O((U + Q) log U) callback work for U update positions.
/// Space: O(Q).

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <numeric>
#include <vector>

namespace noya {

/// @brief Run parallel binary search over prefixes of a common update list.
/// `ok` must be a known true boundary and `ng` a known false boundary. Calling
/// check(q) after t calls to update represents the predicate at prefix t.
template <class Reset, class Update, class Check>
std::vector<int> parallel_binary_search(int query_count, int ok, int ng,
                                        Reset reset, Update update,
                                        Check check) {
  assert(query_count >= 0);
  int update_count = std::max(ok, ng);
  assert(std::min(ok, ng) >= -1);
  std::vector<int> true_bound(query_count, ok);
  std::vector<int> false_bound(query_count, ng);
  while (true) {
    std::vector<std::vector<int>> buckets(update_count + 1);
    int remaining = 0;
    for (int query = 0; query < query_count; query++) {
      if (std::llabs(static_cast<long long>(true_bound[query]) -
                     false_bound[query]) <= 1) {
        continue;
      }
      int middle = std::midpoint(true_bound[query], false_bound[query]);
      assert(0 <= middle && middle <= update_count);
      buckets[middle].push_back(query);
      remaining++;
    }
    if (remaining == 0) {
      break;
    }

    reset();
    int applied = 0;
    for (int prefix = 0; prefix <= update_count; prefix++) {
      while (applied < prefix) {
        update(applied++);
      }
      for (int query : buckets[prefix]) {
        if (check(query)) {
          true_bound[query] = prefix;
        } else {
          false_bound[query] = prefix;
        }
      }
    }
  }
  return true_bound;
}

} // namespace noya

#endif // NOYA_PARALLEL_BINARY_SEARCH_HPP
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <numeric>
#include <vector>

/// @complexity Time: O((U + Q) log U) callback work for U update positions.
/// Space: O(Q).

namespace noya {

/// @brief Run parallel binary search over prefixes of a common update list.
/// `ok` must be a known true boundary and `ng` a known false boundary. Calling
/// check(q) after t calls to update represents the predicate at prefix t.
template <class Reset, class Update, class Check>
std::vector<int> parallel_binary_search(int query_count, int ok, int ng,
                                        Reset reset, Update update,
                                        Check check) {
  assert(query_count >= 0);
  int update_count = std::max(ok, ng);
  assert(std::min(ok, ng) >= -1);
  std::vector<int> true_bound(query_count, ok);
  std::vector<int> false_bound(query_count, ng);
  while (true) {
    std::vector<std::vector<int>> buckets(update_count + 1);
    int remaining = 0;
    for (int query = 0; query < query_count; query++) {
      if (std::llabs(static_cast<long long>(true_bound[query]) -
                     false_bound[query]) <= 1) {
        continue;
      }
      int middle = std::midpoint(true_bound[query], false_bound[query]);
      assert(0 <= middle && middle <= update_count);
      buckets[middle].push_back(query);
      remaining++;
    }
    if (remaining == 0) {
      break;
    }

    reset();
    int applied = 0;
    for (int prefix = 0; prefix <= update_count; prefix++) {
      while (applied < prefix) {
        update(applied++);
      }
      for (int query : buckets[prefix]) {
        if (check(query)) {
          true_bound[query] = prefix;
        } else {
          false_bound[query] = prefix;
        }
      }
    }
  }
  return true_bound;
}

} // namespace noya