Skip to content

sparse_fenwick_2d.hpp

SECTIONData Structure INCLUDEnoya/sparse_fenwick_2d.hpp

在坐标稀疏的二维平面上执行点加与矩形和查询;只为实际出现的坐标分配空间。

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).

跳到代码 · GitHub ↗

Implementation

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

/// @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).

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

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>> pt;
  std::vector<Coordinate> xs;
  std::vector<std::vector<Coordinate>> ys;
  std::vector<std::vector<T>> dat;
  bool ok = false;

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

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

  /// @brief Build the compressed Fenwick nodes with every value initially zero.
  void build() {
    assert(!ok);
    xs.reserve(pt.size());
    for (auto [x, y] : pt) {
      (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] : pt) {
      int idx = int(std::lower_bound(xs.begin(), xs.end(), x) - xs.begin());
      for (idx++; idx <= int(xs.size()); idx += idx & -idx) {
        ys[idx].push_back(y);
      }
    }
    dat.resize(xs.size() + 1);
    for (int idx = 1; idx <= int(xs.size()); idx++) {
      auto &xs0 = ys[idx];
      std::sort(xs0.begin(), xs0.end());
      xs0.erase(std::unique(xs0.begin(), xs0.end()), xs0.end());
      dat[idx].assign(xs0.size() + 1, T{});
    }
    pt.clear();
    pt.shrink_to_fit();
    ok = true;
  }

  /// @brief Add delta at a coordinate registered before build().
  void add(Coordinate x, Coordinate y, const T &dif) {
    assert(ok);
    auto ix = std::lower_bound(xs.begin(), xs.end(), x);
    assert(ix != xs.end() && *ix == x);
    int xi = int(ix - xs.begin()) + 1;
    for (; xi <= int(xs.size()); xi += xi & -xi) {
      auto iy = std::lower_bound(ys[xi].begin(), ys[xi].end(), y);
      assert(iy != ys[xi].end() && *iy == y);
      int yi = int(iy - ys[xi].begin()) + 1;
      for (; yi < int(dat[xi].size()); yi += yi & -yi) {
        dat[xi][yi] += dif;
      }
    }
  }

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

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

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

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

/// @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).

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

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>> pt;
  std::vector<Coordinate> xs;
  std::vector<std::vector<Coordinate>> ys;
  std::vector<std::vector<T>> dat;
  bool ok = false;

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

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

  /// @brief Build the compressed Fenwick nodes with every value initially zero.
  void build() {
    assert(!ok);
    xs.reserve(pt.size());
    for (auto [x, y] : pt) {
      (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] : pt) {
      int idx = int(std::lower_bound(xs.begin(), xs.end(), x) - xs.begin());
      for (idx++; idx <= int(xs.size()); idx += idx & -idx) {
        ys[idx].push_back(y);
      }
    }
    dat.resize(xs.size() + 1);
    for (int idx = 1; idx <= int(xs.size()); idx++) {
      auto &xs0 = ys[idx];
      std::sort(xs0.begin(), xs0.end());
      xs0.erase(std::unique(xs0.begin(), xs0.end()), xs0.end());
      dat[idx].assign(xs0.size() + 1, T{});
    }
    pt.clear();
    pt.shrink_to_fit();
    ok = true;
  }

  /// @brief Add delta at a coordinate registered before build().
  void add(Coordinate x, Coordinate y, const T &dif) {
    assert(ok);
    auto ix = std::lower_bound(xs.begin(), xs.end(), x);
    assert(ix != xs.end() && *ix == x);
    int xi = int(ix - xs.begin()) + 1;
    for (; xi <= int(xs.size()); xi += xi & -xi) {
      auto iy = std::lower_bound(ys[xi].begin(), ys[xi].end(), y);
      assert(iy != ys[xi].end() && *iy == y);
      int yi = int(iy - ys[xi].begin()) + 1;
      for (; yi < int(dat[xi].size()); yi += yi & -yi) {
        dat[xi][yi] += dif;
      }
    }
  }

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

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

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

} // namespace noya

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

/// @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>> pt;
  std::vector<Coordinate> xs;
  std::vector<std::vector<Coordinate>> ys;
  std::vector<std::vector<T>> dat;
  bool ok = false;

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

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

  /// @brief Build the compressed Fenwick nodes with every value initially zero.
  void build() {
    assert(!ok);
    xs.reserve(pt.size());
    for (auto [x, y] : pt) {
      (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] : pt) {
      int idx = int(std::lower_bound(xs.begin(), xs.end(), x) - xs.begin());
      for (idx++; idx <= int(xs.size()); idx += idx & -idx) {
        ys[idx].push_back(y);
      }
    }
    dat.resize(xs.size() + 1);
    for (int idx = 1; idx <= int(xs.size()); idx++) {
      auto &xs0 = ys[idx];
      std::sort(xs0.begin(), xs0.end());
      xs0.erase(std::unique(xs0.begin(), xs0.end()), xs0.end());
      dat[idx].assign(xs0.size() + 1, T{});
    }
    pt.clear();
    pt.shrink_to_fit();
    ok = true;
  }

  /// @brief Add delta at a coordinate registered before build().
  void add(Coordinate x, Coordinate y, const T &dif) {
    assert(ok);
    auto ix = std::lower_bound(xs.begin(), xs.end(), x);
    assert(ix != xs.end() && *ix == x);
    int xi = int(ix - xs.begin()) + 1;
    for (; xi <= int(xs.size()); xi += xi & -xi) {
      auto iy = std::lower_bound(ys[xi].begin(), ys[xi].end(), y);
      assert(iy != ys[xi].end() && *iy == y);
      int yi = int(iy - ys[xi].begin()) + 1;
      for (; yi < int(dat[xi].size()); yi += yi & -yi) {
        dat[xi][yi] += dif;
      }
    }
  }

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

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

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

} // namespace noya