Skip to content

point_add_rectangle_sum.hpp

SECTIONData Structure INCLUDEnoya/point_add_rectangle_sum.hpp

Point-add rectangle-sum data structure on sparse coordinates. All future update coordinates are registered before build(); coordinate compression inside each x-Fenwick node then supports online additions and half-open rectangle sums without allocating the full plane.

Verified by point_add_rectangle_sum.

离线维护二维点权增加与轴对齐矩形求和;题目给出动态加点和矩形统计时可直接套用。

Implementation

View on GitHub

#ifndef NOYA_POINT_ADD_RECTANGLE_SUM_HPP
#define NOYA_POINT_ADD_RECTANGLE_SUM_HPP 1

/// @complexity Time: O(p log^2 p) preprocessing and O(log^2 p) per point
/// addition or rectangle query.  Space: O(p log p).

#include "noya/sparse_fenwick_2d.hpp"

namespace noya {

/// @brief Point-add rectangle-sum data structure on sparse coordinates.
/// All future update coordinates are registered before build(); coordinate
/// compression inside each x-Fenwick node then supports online additions and
/// half-open rectangle sums without allocating the full plane.
template <class T, class Coordinate = int>
using point_add_rectangle_sum = sparse_fenwick_2d<T, Coordinate>;

} // namespace noya

#endif // NOYA_POINT_ADD_RECTANGLE_SUM_HPP
#include <algorithm>
#include <cassert>
#include <utility>
#include <vector>

/// @complexity Time: O(p log^2 p) preprocessing and O(log^2 p) per point
/// addition or rectangle query.  Space: O(p log p).

/// @complexity Time: O(p log^2 p) registration/build and O(log^2 p) per point
/// addition, prefix sum, or rectangle sum.  Space: O(p log p).

namespace noya {

/// @brief Offline-built sparse two-dimensional Fenwick tree.
/// Register every coordinate that may later be updated, then call build().
/// An x-Fenwick node stores only the registered y-coordinates contributing to
/// it, so the dense coordinate plane is replaced by O(p log p) cells.
template <class T, class Coordinate = int> struct sparse_fenwick_2d {
  std::vector<std::pair<Coordinate, Coordinate>> registered;
  std::vector<Coordinate> xs;
  std::vector<std::vector<Coordinate>> ys;
  std::vector<std::vector<T>> data;
  bool built = false;

  /// @brief Remove all registered coordinates and values.
  void clear() {
    registered.clear();
    xs.clear();
    ys.clear();
    data.clear();
    built = false;
  }

  /// @brief Register a coordinate that may be passed to add() after build().
  void reserve(Coordinate x, Coordinate y) {
    assert(!built);
    registered.emplace_back(x, y);
  }

  /// @brief Build the compressed Fenwick nodes with every value initially zero.
  void build() {
    assert(!built);
    xs.reserve(registered.size());
    for (auto [x, y] : registered) {
      (void)y;
      xs.push_back(x);
    }
    std::sort(xs.begin(), xs.end());
    xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
    ys.assign(xs.size() + 1, {});
    for (auto [x, y] : registered) {
      int index = int(std::lower_bound(xs.begin(), xs.end(), x) - xs.begin());
      for (index++; index <= int(xs.size()); index += index & -index) {
        ys[index].push_back(y);
      }
    }
    data.resize(xs.size() + 1);
    for (int index = 1; index <= int(xs.size()); index++) {
      auto &coordinates = ys[index];
      std::sort(coordinates.begin(), coordinates.end());
      coordinates.erase(std::unique(coordinates.begin(), coordinates.end()),
                        coordinates.end());
      data[index].assign(coordinates.size() + 1, T{});
    }
    registered.clear();
    registered.shrink_to_fit();
    built = true;
  }

  /// @brief Add delta at a coordinate registered before build().
  void add(Coordinate x, Coordinate y, const T &delta) {
    assert(built);
    auto x_iterator = std::lower_bound(xs.begin(), xs.end(), x);
    assert(x_iterator != xs.end() && *x_iterator == x);
    int x_index = int(x_iterator - xs.begin()) + 1;
    for (; x_index <= int(xs.size()); x_index += x_index & -x_index) {
      auto y_iterator = std::lower_bound(ys[x_index].begin(), ys[x_index].end(), y);
      assert(y_iterator != ys[x_index].end() && *y_iterator == y);
      int y_index = int(y_iterator - ys[x_index].begin()) + 1;
      for (; y_index < int(data[x_index].size()); y_index += y_index & -y_index) {
        data[x_index][y_index] += delta;
      }
    }
  }

  /// @brief Sum values at coordinates (px, py) with px < x and py < y.
  T prefix_sum(Coordinate x, Coordinate y) const {
    assert(built);
    int x_index = int(std::lower_bound(xs.begin(), xs.end(), x) - xs.begin());
    T result{};
    for (; x_index > 0; x_index -= x_index & -x_index) {
      int y_index = int(std::lower_bound(ys[x_index].begin(), ys[x_index].end(), y) -
                        ys[x_index].begin());
      for (; y_index > 0; y_index -= y_index & -y_index) {
        result += data[x_index][y_index];
      }
    }
    return result;
  }

  /// @brief Sum values at coordinates (px, py) with px <= x and py <= y.
  T prefix_sum_inclusive(Coordinate x, Coordinate y) const {
    assert(built);
    int x_index = int(std::upper_bound(xs.begin(), xs.end(), x) - xs.begin());
    T result{};
    for (; x_index > 0; x_index -= x_index & -x_index) {
      int y_index = int(std::upper_bound(ys[x_index].begin(), ys[x_index].end(), y) -
                        ys[x_index].begin());
      for (; y_index > 0; y_index -= y_index & -y_index) {
        result += data[x_index][y_index];
      }
    }
    return result;
  }

  /// @brief Sum values in [left, right) x [down, up).
  T rectangle_sum(Coordinate left, Coordinate right, Coordinate down,
                  Coordinate up) const {
    return prefix_sum(right, up) - prefix_sum(left, up) -
           prefix_sum(right, down) + prefix_sum(left, down);
  }
};

} // namespace noya

namespace noya {

/// @brief Point-add rectangle-sum data structure on sparse coordinates.
/// All future update coordinates are registered before build(); coordinate
/// compression inside each x-Fenwick node then supports online additions and
/// half-open rectangle sums without allocating the full plane.
template <class T, class Coordinate = int>
using point_add_rectangle_sum = sparse_fenwick_2d<T, Coordinate>;

} // namespace noya