Skip to content

sparse_range_affine_sum.hpp

SECTIONData Structure INCLUDEnoya/sparse_range_affine_sum.hpp

在巨大下标域上维护区间仿射变换与区间和,只为访问过的节点分配内存。

Complexity: Time: O(log coordinate_range) per range update or query. Space: O(u log coordinate_range) after u updates.

AC 记录:range_affine_range_sum_large_array

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(log coordinate_range) per range update or query.
/// Space: O(u log coordinate_range) after u updates.

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <type_traits>
#include <vector>

namespace noya {

/// @brief Dynamic lazy segment tree for an initially zero huge array.  Missing
/// nodes mean an all-zero interval; an affine tag transforms a segment sum by
/// `sum <- mul * sum + add * len`.  Tags are composed in
/// chronological order and queries carry inherited tags without materializing
/// untouched children.
/// Coordinates are integers in the half-open domain [lo, hi).
template <class T, class Coordinate = std::int64_t>
class sparse_range_affine_sum {
  using U = std::conditional_t<(sizeof(Coordinate) > sizeof(std::uint64_t)),
                               unsigned __int128, std::uint64_t>;

  struct node {
    int ls = -1;
    int rs = -1;
    T sum = 0;
    T mul = 1;
    T add = 0;
  };

  Coordinate lo_ = 0;
  Coordinate hi_ = 0;
  std::vector<node> tr;

  int new_node() {
    tr.push_back({});
    return int(tr.size()) - 1;
  }

  static U span(Coordinate l, Coordinate r) { return U(r) - U(l); }

  // Integer average rounded down, including GCC's extended integer types.
  static Coordinate midpoint(Coordinate l, Coordinate r) {
    return (l & r) + ((l ^ r) >> 1);
  }

  void apply_to(int cur, U len, const T &mul, const T &add) {
    node &tar = tr[cur];
    tar.sum = mul * tar.sum + add * T(len);
    tar.mul = mul * tar.mul;
    tar.add = mul * tar.add + add;
  }

  void push(int cur, Coordinate low, Coordinate big) {
    if (span(low, big) == 1) {
      return;
    }
    if (tr[cur].mul == T(1) && tr[cur].add == T(0)) {
      return;
    }
    Coordinate mid = midpoint(low, big);
    if (tr[cur].ls == -1) {
      int ch = new_node();
      tr[cur].ls = ch;
    }
    if (tr[cur].rs == -1) {
      int ch = new_node();
      tr[cur].rs = ch;
    }
    T mul = tr[cur].mul;
    T add = tr[cur].add;
    int l = tr[cur].ls;
    int r = tr[cur].rs;
    apply_to(l, span(low, mid), mul, add);
    apply_to(r, span(mid, big), mul, add);
    tr[cur].mul = T(1);
    tr[cur].add = T(0);
  }

  T node_sum(int cur) const { return cur == -1 ? T(0) : tr[cur].sum; }

  int apply_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
                Coordinate r, const T &mul, const T &add) {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r) {
      return cur;
    }
    if (cur == -1) {
      cur = new_node();
    }
    if (l == low && r == big) {
      apply_to(cur, span(low, big), mul, add);
      return cur;
    }
    push(cur, low, big);
    Coordinate mid = midpoint(low, big);
    int lc = tr[cur].ls;
    int rc = tr[cur].rs;
    lc = apply_rec(lc, low, mid, l, r, mul, add);
    rc = apply_rec(rc, mid, big, l, r, mul, add);
    tr[cur].ls = lc;
    tr[cur].rs = rc;
    tr[cur].sum = node_sum(lc) + node_sum(rc);
    return cur;
  }

  T prod_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
             Coordinate r, T am, T aa) const {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r) {
      return T(0);
    }
    if (cur == -1) {
      return aa * T(span(l, r));
    }
    const node &tar = tr[cur];
    if (l == low && r == big) {
      return am * tar.sum + aa * T(span(low, big));
    }
    aa = am * tar.add + aa;
    am *= tar.mul;
    Coordinate mid = midpoint(low, big);
    return prod_rec(tar.ls, low, mid, l, r, am, aa) +
           prod_rec(tar.rs, mid, big, l, r, am, aa);
  }

public:
  sparse_range_affine_sum() = default;
  sparse_range_affine_sum(Coordinate lo, Coordinate hi, std::size_t cap = 0)
      : lo_(lo), hi_(hi) {
    assert(lo_ < hi_);
    tr.reserve(cap);
    new_node();
  }

  int nodes() const { return int(tr.size()); }

  void apply(Coordinate l, Coordinate r, const T &mul, const T &add) {
    assert(lo_ <= l && l <= r && r <= hi_);
    apply_rec(0, lo_, hi_, l, r, mul, add);
  }

  T prod(Coordinate l, Coordinate r) const {
    assert(lo_ <= l && l <= r && r <= hi_);
    return prod_rec(0, lo_, hi_, l, r, T(1), T(0));
  }

  T all_prod() const { return tr.empty() ? T(0) : tr[0].sum; }
};

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

/// @complexity Time: O(log coordinate_range) per range update or query.
/// Space: O(u log coordinate_range) after u updates.

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <type_traits>
#include <vector>

namespace noya {

/// @brief Dynamic lazy segment tree for an initially zero huge array.  Missing
/// nodes mean an all-zero interval; an affine tag transforms a segment sum by
/// `sum <- mul * sum + add * len`.  Tags are composed in
/// chronological order and queries carry inherited tags without materializing
/// untouched children.
/// Coordinates are integers in the half-open domain [lo, hi).
template <class T, class Coordinate = std::int64_t>
class sparse_range_affine_sum {
  using U = std::conditional_t<(sizeof(Coordinate) > sizeof(std::uint64_t)),
                               unsigned __int128, std::uint64_t>;

  struct node {
    int ls = -1;
    int rs = -1;
    T sum = 0;
    T mul = 1;
    T add = 0;
  };

  Coordinate lo_ = 0;
  Coordinate hi_ = 0;
  std::vector<node> tr;

  int new_node() {
    tr.push_back({});
    return int(tr.size()) - 1;
  }

  static U span(Coordinate l, Coordinate r) { return U(r) - U(l); }

  // Integer average rounded down, including GCC's extended integer types.
  static Coordinate midpoint(Coordinate l, Coordinate r) {
    return (l & r) + ((l ^ r) >> 1);
  }

  void apply_to(int cur, U len, const T &mul, const T &add) {
    node &tar = tr[cur];
    tar.sum = mul * tar.sum + add * T(len);
    tar.mul = mul * tar.mul;
    tar.add = mul * tar.add + add;
  }

  void push(int cur, Coordinate low, Coordinate big) {
    if (span(low, big) == 1) {
      return;
    }
    if (tr[cur].mul == T(1) && tr[cur].add == T(0)) {
      return;
    }
    Coordinate mid = midpoint(low, big);
    if (tr[cur].ls == -1) {
      int ch = new_node();
      tr[cur].ls = ch;
    }
    if (tr[cur].rs == -1) {
      int ch = new_node();
      tr[cur].rs = ch;
    }
    T mul = tr[cur].mul;
    T add = tr[cur].add;
    int l = tr[cur].ls;
    int r = tr[cur].rs;
    apply_to(l, span(low, mid), mul, add);
    apply_to(r, span(mid, big), mul, add);
    tr[cur].mul = T(1);
    tr[cur].add = T(0);
  }

  T node_sum(int cur) const { return cur == -1 ? T(0) : tr[cur].sum; }

  int apply_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
                Coordinate r, const T &mul, const T &add) {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r) {
      return cur;
    }
    if (cur == -1) {
      cur = new_node();
    }
    if (l == low && r == big) {
      apply_to(cur, span(low, big), mul, add);
      return cur;
    }
    push(cur, low, big);
    Coordinate mid = midpoint(low, big);
    int lc = tr[cur].ls;
    int rc = tr[cur].rs;
    lc = apply_rec(lc, low, mid, l, r, mul, add);
    rc = apply_rec(rc, mid, big, l, r, mul, add);
    tr[cur].ls = lc;
    tr[cur].rs = rc;
    tr[cur].sum = node_sum(lc) + node_sum(rc);
    return cur;
  }

  T prod_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
             Coordinate r, T am, T aa) const {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r) {
      return T(0);
    }
    if (cur == -1) {
      return aa * T(span(l, r));
    }
    const node &tar = tr[cur];
    if (l == low && r == big) {
      return am * tar.sum + aa * T(span(low, big));
    }
    aa = am * tar.add + aa;
    am *= tar.mul;
    Coordinate mid = midpoint(low, big);
    return prod_rec(tar.ls, low, mid, l, r, am, aa) +
           prod_rec(tar.rs, mid, big, l, r, am, aa);
  }

public:
  sparse_range_affine_sum() = default;
  sparse_range_affine_sum(Coordinate lo, Coordinate hi, std::size_t cap = 0)
      : lo_(lo), hi_(hi) {
    assert(lo_ < hi_);
    tr.reserve(cap);
    new_node();
  }

  int nodes() const { return int(tr.size()); }

  void apply(Coordinate l, Coordinate r, const T &mul, const T &add) {
    assert(lo_ <= l && l <= r && r <= hi_);
    apply_rec(0, lo_, hi_, l, r, mul, add);
  }

  T prod(Coordinate l, Coordinate r) const {
    assert(lo_ <= l && l <= r && r <= hi_);
    return prod_rec(0, lo_, hi_, l, r, T(1), T(0));
  }

  T all_prod() const { return tr.empty() ? T(0) : tr[0].sum; }
};

} // namespace noya

#endif // NOYA_SPARSE_RANGE_AFFINE_SUM_HPP
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <type_traits>
#include <vector>

/// @complexity Time: O(log coordinate_range) per range update or query.
/// Space: O(u log coordinate_range) after u updates.

namespace noya {

/// @brief Dynamic lazy segment tree for an initially zero huge array.  Missing
/// nodes mean an all-zero interval; an affine tag transforms a segment sum by
/// `sum <- mul * sum + add * len`.  Tags are composed in
/// chronological order and queries carry inherited tags without materializing
/// untouched children.
/// Coordinates are integers in the half-open domain [lo, hi).
template <class T, class Coordinate = std::int64_t>
class sparse_range_affine_sum {
  using U = std::conditional_t<(sizeof(Coordinate) > sizeof(std::uint64_t)),
                               unsigned __int128, std::uint64_t>;

  struct node {
    int ls = -1;
    int rs = -1;
    T sum = 0;
    T mul = 1;
    T add = 0;
  };

  Coordinate lo_ = 0;
  Coordinate hi_ = 0;
  std::vector<node> tr;

  int new_node() {
    tr.push_back({});
    return int(tr.size()) - 1;
  }

  static U span(Coordinate l, Coordinate r) { return U(r) - U(l); }

  // Integer average rounded down, including GCC's extended integer types.
  static Coordinate midpoint(Coordinate l, Coordinate r) {
    return (l & r) + ((l ^ r) >> 1);
  }

  void apply_to(int cur, U len, const T &mul, const T &add) {
    node &tar = tr[cur];
    tar.sum = mul * tar.sum + add * T(len);
    tar.mul = mul * tar.mul;
    tar.add = mul * tar.add + add;
  }

  void push(int cur, Coordinate low, Coordinate big) {
    if (span(low, big) == 1) {
      return;
    }
    if (tr[cur].mul == T(1) && tr[cur].add == T(0)) {
      return;
    }
    Coordinate mid = midpoint(low, big);
    if (tr[cur].ls == -1) {
      int ch = new_node();
      tr[cur].ls = ch;
    }
    if (tr[cur].rs == -1) {
      int ch = new_node();
      tr[cur].rs = ch;
    }
    T mul = tr[cur].mul;
    T add = tr[cur].add;
    int l = tr[cur].ls;
    int r = tr[cur].rs;
    apply_to(l, span(low, mid), mul, add);
    apply_to(r, span(mid, big), mul, add);
    tr[cur].mul = T(1);
    tr[cur].add = T(0);
  }

  T node_sum(int cur) const { return cur == -1 ? T(0) : tr[cur].sum; }

  int apply_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
                Coordinate r, const T &mul, const T &add) {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r) {
      return cur;
    }
    if (cur == -1) {
      cur = new_node();
    }
    if (l == low && r == big) {
      apply_to(cur, span(low, big), mul, add);
      return cur;
    }
    push(cur, low, big);
    Coordinate mid = midpoint(low, big);
    int lc = tr[cur].ls;
    int rc = tr[cur].rs;
    lc = apply_rec(lc, low, mid, l, r, mul, add);
    rc = apply_rec(rc, mid, big, l, r, mul, add);
    tr[cur].ls = lc;
    tr[cur].rs = rc;
    tr[cur].sum = node_sum(lc) + node_sum(rc);
    return cur;
  }

  T prod_rec(int cur, Coordinate low, Coordinate big, Coordinate l,
             Coordinate r, T am, T aa) const {
    l = std::max(l, low);
    r = std::min(r, big);
    if (l >= r) {
      return T(0);
    }
    if (cur == -1) {
      return aa * T(span(l, r));
    }
    const node &tar = tr[cur];
    if (l == low && r == big) {
      return am * tar.sum + aa * T(span(low, big));
    }
    aa = am * tar.add + aa;
    am *= tar.mul;
    Coordinate mid = midpoint(low, big);
    return prod_rec(tar.ls, low, mid, l, r, am, aa) +
           prod_rec(tar.rs, mid, big, l, r, am, aa);
  }

public:
  sparse_range_affine_sum() = default;
  sparse_range_affine_sum(Coordinate lo, Coordinate hi, std::size_t cap = 0)
      : lo_(lo), hi_(hi) {
    assert(lo_ < hi_);
    tr.reserve(cap);
    new_node();
  }

  int nodes() const { return int(tr.size()); }

  void apply(Coordinate l, Coordinate r, const T &mul, const T &add) {
    assert(lo_ <= l && l <= r && r <= hi_);
    apply_rec(0, lo_, hi_, l, r, mul, add);
  }

  T prod(Coordinate l, Coordinate r) const {
    assert(lo_ <= l && l <= r && r <= hi_);
    return prod_rec(0, lo_, hi_, l, r, T(1), T(0));
  }

  T all_prod() const { return tr.empty() ? T(0) : tr[0].sum; }
};

} // namespace noya