Skip to content

sparse_fenwick_2d.hpp

SECTIONData Structure INCLUDEnoya/ds/sparse_fenwick_2d.hpp

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

Complexity: Time: amortized \(O(1)\) registration, \(O(p \log (p + 1))\) build and \(O(\log^{2} (p + 1))\) addition/query. Space: \(O(1 + p \log (p + 1))\) cells.

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: amortized O(1) registration, O(p log(p+1)) build and
/// O(log^2(p+1)) addition/query. Space: O(1 + p log(p+1)) cells.

#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, {});
    // Global y order makes every node's coordinate list sorted as it is built.
    std::sort(pt.begin(), pt.end(), [](const auto &a, const auto &b) {
      return a.second < b.second;
    });
    for (const 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) {
        // A previous visit with this y also populated every higher ancestor.
        if (!ys[idx].empty() && ys[idx].back() == y) break;
        ys[idx].push_back(y);
      }
    }
    dat.resize(xs.size() + 1);
    for (int idx = 1; idx <= int(xs.size()); idx++) {
      dat[idx].assign(ys[idx].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 {
    assert(ok);
    if (l == r || dn == up) return T{};
    int xl = int(std::lower_bound(xs.begin(), xs.end(), l) - xs.begin());
    int xr = int(std::lower_bound(xs.begin(), xs.end(), r) - xs.begin());
    // Cancel shared ancestors of the two prefix paths in each dimension.
    auto row_sum = [&](int x) {
      const auto &v = ys[x];
      int a = int(std::lower_bound(v.begin(), v.end(), dn) - v.begin());
      int b = int(std::lower_bound(v.begin(), v.end(), up) - v.begin());
      T sum{};
      while (a != b) {
        if (a < b) {
          sum += dat[x][b];
          b -= b & -b;
        } else {
          sum = sum - dat[x][a];
          a -= a & -a;
        }
      }
      return sum;
    };
    T sum{};
    while (xl != xr) {
      if (xl < xr) {
        sum += row_sum(xr);
        xr -= xr & -xr;
      } else {
        sum = sum - row_sum(xl);
        xl -= xl & -xl;
      }
    }
    return sum;
  }
};

} // namespace noya

 // NOYA_SPARSE_FENWICK_2D_HPP
#ifndef NOYA_SPARSE_FENWICK_2D_HPP
#define NOYA_SPARSE_FENWICK_2D_HPP 1

/// @complexity Time: amortized O(1) registration, O(p log(p+1)) build and
/// O(log^2(p+1)) addition/query. Space: O(1 + p log(p+1)) cells.

#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, {});
    // Global y order makes every node's coordinate list sorted as it is built.
    std::sort(pt.begin(), pt.end(), [](const auto &a, const auto &b) {
      return a.second < b.second;
    });
    for (const 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) {
        // A previous visit with this y also populated every higher ancestor.
        if (!ys[idx].empty() && ys[idx].back() == y) break;
        ys[idx].push_back(y);
      }
    }
    dat.resize(xs.size() + 1);
    for (int idx = 1; idx <= int(xs.size()); idx++) {
      dat[idx].assign(ys[idx].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 {
    assert(ok);
    if (l == r || dn == up) return T{};
    int xl = int(std::lower_bound(xs.begin(), xs.end(), l) - xs.begin());
    int xr = int(std::lower_bound(xs.begin(), xs.end(), r) - xs.begin());
    // Cancel shared ancestors of the two prefix paths in each dimension.
    auto row_sum = [&](int x) {
      const auto &v = ys[x];
      int a = int(std::lower_bound(v.begin(), v.end(), dn) - v.begin());
      int b = int(std::lower_bound(v.begin(), v.end(), up) - v.begin());
      T sum{};
      while (a != b) {
        if (a < b) {
          sum += dat[x][b];
          b -= b & -b;
        } else {
          sum = sum - dat[x][a];
          a -= a & -a;
        }
      }
      return sum;
    };
    T sum{};
    while (xl != xr) {
      if (xl < xr) {
        sum += row_sum(xr);
        xr -= xr & -xr;
      } else {
        sum = sum - row_sum(xl);
        xl -= xl & -xl;
      }
    }
    return sum;
  }
};

} // namespace noya

#endif // NOYA_SPARSE_FENWICK_2D_HPP
/// @complexity Time: amortized O(1) registration, O(p log(p+1)) build and
/// O(log^2(p+1)) addition/query. Space: O(1 + p log(p+1)) cells.

#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, {});
    // Global y order makes every node's coordinate list sorted as it is built.
    std::sort(pt.begin(), pt.end(), [](const auto &a, const auto &b) {
      return a.second < b.second;
    });
    for (const 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) {
        // A previous visit with this y also populated every higher ancestor.
        if (!ys[idx].empty() && ys[idx].back() == y) break;
        ys[idx].push_back(y);
      }
    }
    dat.resize(xs.size() + 1);
    for (int idx = 1; idx <= int(xs.size()); idx++) {
      dat[idx].assign(ys[idx].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 {
    assert(ok);
    if (l == r || dn == up) return T{};
    int xl = int(std::lower_bound(xs.begin(), xs.end(), l) - xs.begin());
    int xr = int(std::lower_bound(xs.begin(), xs.end(), r) - xs.begin());
    // Cancel shared ancestors of the two prefix paths in each dimension.
    auto row_sum = [&](int x) {
      const auto &v = ys[x];
      int a = int(std::lower_bound(v.begin(), v.end(), dn) - v.begin());
      int b = int(std::lower_bound(v.begin(), v.end(), up) - v.begin());
      T sum{};
      while (a != b) {
        if (a < b) {
          sum += dat[x][b];
          b -= b & -b;
        } else {
          sum = sum - dat[x][a];
          a -= a & -a;
        }
      }
      return sum;
    };
    T sum{};
    while (xl != xr) {
      if (xl < xr) {
        sum += row_sum(xr);
        xr -= xr & -xr;
      } else {
        sum = sum - row_sum(xl);
        xl -= xl & -xl;
      }
    }
    return sum;
  }
};

} // namespace noya

 // NOYA_SPARSE_FENWICK_2D_HPP