Skip to content

persistent_affine_segtree.hpp

SECTIONData Structure INCLUDEnoya/ds/persistent_affine_segtree.hpp

保留每次区间仿射修改后的线段树版本,并查询任一版本的区间和。

Complexity: Time: \(O(\log n)\) per range affine update, range copy, or query. Space: \(O(n + q \log n)\) nodes for \(q\) derived versions.

AC 记录:persistent_range_affine_range_sum。

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(log n) per range affine update, range copy, or query.
/// Space: O(n + q log n) nodes for q derived versions.

#include <cassert>
#include <vector>

namespace noya {

/// @brief Persistent lazy segment tree for range affine transforms and sums.
/// Updates clone O(log n) nodes and share untouched subtrees. A range copy descends in a
/// destination version and a source version together, carrying source tags
/// without modifying source nodes. Destination tags are pushed only into fresh
/// clones, so every older root remains unchanged.
template <class T> class persistent_affine_segtree {
public:
  persistent_affine_segtree() = default;

  explicit persistent_affine_segtree(const std::vector<T> &a) { build(a); }

  void build(const std::vector<T> &a) {
    assert(!a.empty());
    n = int(a.size());
    tr.clear();
    tr.reserve(a.size() * 2);
    rt0 = build_node(a, 0, n);
  }

  void reserve(int cap) { tr.reserve(cap); }

  int initial_root() const { return rt0; }

  int apply(int rt, int l, int r, T mul, T add) {
    assert(valid_root(rt));
    assert(0 <= l && l <= r && r <= n);
    if (l == r || (mul == T(1) && add == T(0))) {
      return rt;
    }
    return apply_node(rt, 0, n, l, r, mul, add);
  }

  int copy_range(int dr, int sr, int l, int r) {
    assert(valid_root(dr) && valid_root(sr));
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return dr;
    }
    return copy_node(dr, sr, 0, n, l, r, T(1), T(0));
  }

  T prod(int rt, int l, int r) const {
    assert(valid_root(rt));
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return T(0);
    }
    return prod_node(rt, 0, n, l, r, T(1), T(0));
  }

private:
  struct node {
    T sum{};
    T mul = T(1);
    T add{};
    int ls = -1;
    int rs = -1;
  };

  int n = 0;
  int rt0 = -1;
  std::vector<node> tr;

  bool valid_root(int rt) const { return 0 <= rt && rt < int(tr.size()); }

  int make_node(const node &val) {
    tr.push_back(val);
    return int(tr.size()) - 1;
  }

  int clone_node(int id) { return make_node(tr[id]); }

  int build_node(const std::vector<T> &a, int l, int r) {
    if (r - l == 1) {
      return make_node(node{.sum = a[l]});
    }
    int mid = l + (r - l) / 2;
    int arr = build_node(a, l, mid);
    int b = build_node(a, mid, r);
    return make_node(node{.sum = tr[arr].sum + tr[b].sum, .ls = arr, .rs = b});
  }

  void apply_here(int id, int len, T mul, T add) {
    tr[id].sum = mul * tr[id].sum + add * T(len);
    tr[id].mul *= mul;
    tr[id].add = mul * tr[id].add + add;
  }

  // Return whether both children were freshly cloned by this push.
  bool push_cloned(int id, int ll, int lr) {
    if (tr[id].ls < 0) {
      return false;
    }
    T mul = tr[id].mul;
    T add = tr[id].add;
    if (mul == T(1) && add == T(0)) {
      return false;
    }
    int arr = clone_node(tr[id].ls);
    int b = clone_node(tr[id].rs);
    tr[id].ls = arr;
    tr[id].rs = b;
    apply_here(arr, ll, mul, add);
    apply_here(b, lr, mul, add);
    tr[id].mul = T(1);
    tr[id].add = T(0);
    return true;
  }

  void pull(int id) { tr[id].sum = tr[tr[id].ls].sum + tr[tr[id].rs].sum; }

  int apply_node(int id, int l, int r, int ql, int qr, T mul, T add,
                 bool owned = false) {
    if (qr <= l || r <= ql) {
      return id;
    }
    int res = owned ? id : clone_node(id);
    if (ql <= l && r <= qr) {
      apply_here(res, r - l, mul, add);
      return res;
    }
    int mid = l + (r - l) / 2;
    bool fresh = push_cloned(res, mid - l, r - mid);
    int arr = apply_node(tr[res].ls, l, mid, ql, qr, mul, add, fresh);
    int b = apply_node(tr[res].rs, mid, r, ql, qr, mul, add, fresh);
    tr[res].ls = arr;
    tr[res].rs = b;
    pull(res);
    return res;
  }

  int copy_node(int dst, int src, int l, int r, int ql, int qr, T sm, T sa,
                bool owned = false) {
    if (qr <= l || r <= ql || (dst == src && sm == T(1) && sa == T(0))) {
      return dst;
    }
    if (ql <= l && r <= qr) {
      if (sm == T(1) && sa == T(0)) {
        return src;
      }
      int res = clone_node(src);
      apply_here(res, r - l, sm, sa);
      return res;
    }
    int mid = l + (r - l) / 2;
    int dc = owned ? dst : clone_node(dst);
    bool fresh = push_cloned(dc, mid - l, r - mid);
    T mul = sm * tr[src].mul;
    T add = sm * tr[src].add + sa;
    int arr = copy_node(tr[dc].ls, tr[src].ls, l, mid, ql, qr, mul, add, fresh);
    int b = copy_node(tr[dc].rs, tr[src].rs, mid, r, ql, qr, mul, add, fresh);
    tr[dc].ls = arr;
    tr[dc].rs = b;
    pull(dc);
    return dc;
  }

  T prod_node(int id, int l, int r, int ql, int qr, T am, T aa) const {
    if (qr <= l || r <= ql) {
      return T(0);
    }
    if (ql <= l && r <= qr) {
      return am * tr[id].sum + aa * T(r - l);
    }
    T mul = am * tr[id].mul;
    T add = am * tr[id].add + aa;
    int mid = l + (r - l) / 2;
    if (qr <= mid) {
      return prod_node(tr[id].ls, l, mid, ql, qr, mul, add);
    }
    if (mid <= ql) {
      return prod_node(tr[id].rs, mid, r, ql, qr, mul, add);
    }
    return prod_node(tr[id].ls, l, mid, ql, qr, mul, add) +
           prod_node(tr[id].rs, mid, r, ql, qr, mul, add);
  }
};

} // namespace noya

 // NOYA_PERSISTENT_AFFINE_SEGTREE_HPP
#ifndef NOYA_PERSISTENT_AFFINE_SEGTREE_HPP
#define NOYA_PERSISTENT_AFFINE_SEGTREE_HPP 1

/// @complexity Time: O(log n) per range affine update, range copy, or query.
/// Space: O(n + q log n) nodes for q derived versions.

#include <cassert>
#include <vector>

namespace noya {

/// @brief Persistent lazy segment tree for range affine transforms and sums.
/// Updates clone O(log n) nodes and share untouched subtrees. A range copy descends in a
/// destination version and a source version together, carrying source tags
/// without modifying source nodes. Destination tags are pushed only into fresh
/// clones, so every older root remains unchanged.
template <class T> class persistent_affine_segtree {
public:
  persistent_affine_segtree() = default;

  explicit persistent_affine_segtree(const std::vector<T> &a) { build(a); }

  void build(const std::vector<T> &a) {
    assert(!a.empty());
    n = int(a.size());
    tr.clear();
    tr.reserve(a.size() * 2);
    rt0 = build_node(a, 0, n);
  }

  void reserve(int cap) { tr.reserve(cap); }

  int initial_root() const { return rt0; }

  int apply(int rt, int l, int r, T mul, T add) {
    assert(valid_root(rt));
    assert(0 <= l && l <= r && r <= n);
    if (l == r || (mul == T(1) && add == T(0))) {
      return rt;
    }
    return apply_node(rt, 0, n, l, r, mul, add);
  }

  int copy_range(int dr, int sr, int l, int r) {
    assert(valid_root(dr) && valid_root(sr));
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return dr;
    }
    return copy_node(dr, sr, 0, n, l, r, T(1), T(0));
  }

  T prod(int rt, int l, int r) const {
    assert(valid_root(rt));
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return T(0);
    }
    return prod_node(rt, 0, n, l, r, T(1), T(0));
  }

private:
  struct node {
    T sum{};
    T mul = T(1);
    T add{};
    int ls = -1;
    int rs = -1;
  };

  int n = 0;
  int rt0 = -1;
  std::vector<node> tr;

  bool valid_root(int rt) const { return 0 <= rt && rt < int(tr.size()); }

  int make_node(const node &val) {
    tr.push_back(val);
    return int(tr.size()) - 1;
  }

  int clone_node(int id) { return make_node(tr[id]); }

  int build_node(const std::vector<T> &a, int l, int r) {
    if (r - l == 1) {
      return make_node(node{.sum = a[l]});
    }
    int mid = l + (r - l) / 2;
    int arr = build_node(a, l, mid);
    int b = build_node(a, mid, r);
    return make_node(node{.sum = tr[arr].sum + tr[b].sum, .ls = arr, .rs = b});
  }

  void apply_here(int id, int len, T mul, T add) {
    tr[id].sum = mul * tr[id].sum + add * T(len);
    tr[id].mul *= mul;
    tr[id].add = mul * tr[id].add + add;
  }

  // Return whether both children were freshly cloned by this push.
  bool push_cloned(int id, int ll, int lr) {
    if (tr[id].ls < 0) {
      return false;
    }
    T mul = tr[id].mul;
    T add = tr[id].add;
    if (mul == T(1) && add == T(0)) {
      return false;
    }
    int arr = clone_node(tr[id].ls);
    int b = clone_node(tr[id].rs);
    tr[id].ls = arr;
    tr[id].rs = b;
    apply_here(arr, ll, mul, add);
    apply_here(b, lr, mul, add);
    tr[id].mul = T(1);
    tr[id].add = T(0);
    return true;
  }

  void pull(int id) { tr[id].sum = tr[tr[id].ls].sum + tr[tr[id].rs].sum; }

  int apply_node(int id, int l, int r, int ql, int qr, T mul, T add,
                 bool owned = false) {
    if (qr <= l || r <= ql) {
      return id;
    }
    int res = owned ? id : clone_node(id);
    if (ql <= l && r <= qr) {
      apply_here(res, r - l, mul, add);
      return res;
    }
    int mid = l + (r - l) / 2;
    bool fresh = push_cloned(res, mid - l, r - mid);
    int arr = apply_node(tr[res].ls, l, mid, ql, qr, mul, add, fresh);
    int b = apply_node(tr[res].rs, mid, r, ql, qr, mul, add, fresh);
    tr[res].ls = arr;
    tr[res].rs = b;
    pull(res);
    return res;
  }

  int copy_node(int dst, int src, int l, int r, int ql, int qr, T sm, T sa,
                bool owned = false) {
    if (qr <= l || r <= ql || (dst == src && sm == T(1) && sa == T(0))) {
      return dst;
    }
    if (ql <= l && r <= qr) {
      if (sm == T(1) && sa == T(0)) {
        return src;
      }
      int res = clone_node(src);
      apply_here(res, r - l, sm, sa);
      return res;
    }
    int mid = l + (r - l) / 2;
    int dc = owned ? dst : clone_node(dst);
    bool fresh = push_cloned(dc, mid - l, r - mid);
    T mul = sm * tr[src].mul;
    T add = sm * tr[src].add + sa;
    int arr = copy_node(tr[dc].ls, tr[src].ls, l, mid, ql, qr, mul, add, fresh);
    int b = copy_node(tr[dc].rs, tr[src].rs, mid, r, ql, qr, mul, add, fresh);
    tr[dc].ls = arr;
    tr[dc].rs = b;
    pull(dc);
    return dc;
  }

  T prod_node(int id, int l, int r, int ql, int qr, T am, T aa) const {
    if (qr <= l || r <= ql) {
      return T(0);
    }
    if (ql <= l && r <= qr) {
      return am * tr[id].sum + aa * T(r - l);
    }
    T mul = am * tr[id].mul;
    T add = am * tr[id].add + aa;
    int mid = l + (r - l) / 2;
    if (qr <= mid) {
      return prod_node(tr[id].ls, l, mid, ql, qr, mul, add);
    }
    if (mid <= ql) {
      return prod_node(tr[id].rs, mid, r, ql, qr, mul, add);
    }
    return prod_node(tr[id].ls, l, mid, ql, qr, mul, add) +
           prod_node(tr[id].rs, mid, r, ql, qr, mul, add);
  }
};

} // namespace noya

#endif // NOYA_PERSISTENT_AFFINE_SEGTREE_HPP
/// @complexity Time: O(log n) per range affine update, range copy, or query.
/// Space: O(n + q log n) nodes for q derived versions.

#include <cassert>
#include <vector>

namespace noya {

/// @brief Persistent lazy segment tree for range affine transforms and sums.
/// Updates clone O(log n) nodes and share untouched subtrees. A range copy descends in a
/// destination version and a source version together, carrying source tags
/// without modifying source nodes. Destination tags are pushed only into fresh
/// clones, so every older root remains unchanged.
template <class T> class persistent_affine_segtree {
public:
  persistent_affine_segtree() = default;

  explicit persistent_affine_segtree(const std::vector<T> &a) { build(a); }

  void build(const std::vector<T> &a) {
    assert(!a.empty());
    n = int(a.size());
    tr.clear();
    tr.reserve(a.size() * 2);
    rt0 = build_node(a, 0, n);
  }

  void reserve(int cap) { tr.reserve(cap); }

  int initial_root() const { return rt0; }

  int apply(int rt, int l, int r, T mul, T add) {
    assert(valid_root(rt));
    assert(0 <= l && l <= r && r <= n);
    if (l == r || (mul == T(1) && add == T(0))) {
      return rt;
    }
    return apply_node(rt, 0, n, l, r, mul, add);
  }

  int copy_range(int dr, int sr, int l, int r) {
    assert(valid_root(dr) && valid_root(sr));
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return dr;
    }
    return copy_node(dr, sr, 0, n, l, r, T(1), T(0));
  }

  T prod(int rt, int l, int r) const {
    assert(valid_root(rt));
    assert(0 <= l && l <= r && r <= n);
    if (l == r) {
      return T(0);
    }
    return prod_node(rt, 0, n, l, r, T(1), T(0));
  }

private:
  struct node {
    T sum{};
    T mul = T(1);
    T add{};
    int ls = -1;
    int rs = -1;
  };

  int n = 0;
  int rt0 = -1;
  std::vector<node> tr;

  bool valid_root(int rt) const { return 0 <= rt && rt < int(tr.size()); }

  int make_node(const node &val) {
    tr.push_back(val);
    return int(tr.size()) - 1;
  }

  int clone_node(int id) { return make_node(tr[id]); }

  int build_node(const std::vector<T> &a, int l, int r) {
    if (r - l == 1) {
      return make_node(node{.sum = a[l]});
    }
    int mid = l + (r - l) / 2;
    int arr = build_node(a, l, mid);
    int b = build_node(a, mid, r);
    return make_node(node{.sum = tr[arr].sum + tr[b].sum, .ls = arr, .rs = b});
  }

  void apply_here(int id, int len, T mul, T add) {
    tr[id].sum = mul * tr[id].sum + add * T(len);
    tr[id].mul *= mul;
    tr[id].add = mul * tr[id].add + add;
  }

  // Return whether both children were freshly cloned by this push.
  bool push_cloned(int id, int ll, int lr) {
    if (tr[id].ls < 0) {
      return false;
    }
    T mul = tr[id].mul;
    T add = tr[id].add;
    if (mul == T(1) && add == T(0)) {
      return false;
    }
    int arr = clone_node(tr[id].ls);
    int b = clone_node(tr[id].rs);
    tr[id].ls = arr;
    tr[id].rs = b;
    apply_here(arr, ll, mul, add);
    apply_here(b, lr, mul, add);
    tr[id].mul = T(1);
    tr[id].add = T(0);
    return true;
  }

  void pull(int id) { tr[id].sum = tr[tr[id].ls].sum + tr[tr[id].rs].sum; }

  int apply_node(int id, int l, int r, int ql, int qr, T mul, T add,
                 bool owned = false) {
    if (qr <= l || r <= ql) {
      return id;
    }
    int res = owned ? id : clone_node(id);
    if (ql <= l && r <= qr) {
      apply_here(res, r - l, mul, add);
      return res;
    }
    int mid = l + (r - l) / 2;
    bool fresh = push_cloned(res, mid - l, r - mid);
    int arr = apply_node(tr[res].ls, l, mid, ql, qr, mul, add, fresh);
    int b = apply_node(tr[res].rs, mid, r, ql, qr, mul, add, fresh);
    tr[res].ls = arr;
    tr[res].rs = b;
    pull(res);
    return res;
  }

  int copy_node(int dst, int src, int l, int r, int ql, int qr, T sm, T sa,
                bool owned = false) {
    if (qr <= l || r <= ql || (dst == src && sm == T(1) && sa == T(0))) {
      return dst;
    }
    if (ql <= l && r <= qr) {
      if (sm == T(1) && sa == T(0)) {
        return src;
      }
      int res = clone_node(src);
      apply_here(res, r - l, sm, sa);
      return res;
    }
    int mid = l + (r - l) / 2;
    int dc = owned ? dst : clone_node(dst);
    bool fresh = push_cloned(dc, mid - l, r - mid);
    T mul = sm * tr[src].mul;
    T add = sm * tr[src].add + sa;
    int arr = copy_node(tr[dc].ls, tr[src].ls, l, mid, ql, qr, mul, add, fresh);
    int b = copy_node(tr[dc].rs, tr[src].rs, mid, r, ql, qr, mul, add, fresh);
    tr[dc].ls = arr;
    tr[dc].rs = b;
    pull(dc);
    return dc;
  }

  T prod_node(int id, int l, int r, int ql, int qr, T am, T aa) const {
    if (qr <= l || r <= ql) {
      return T(0);
    }
    if (ql <= l && r <= qr) {
      return am * tr[id].sum + aa * T(r - l);
    }
    T mul = am * tr[id].mul;
    T add = am * tr[id].add + aa;
    int mid = l + (r - l) / 2;
    if (qr <= mid) {
      return prod_node(tr[id].ls, l, mid, ql, qr, mul, add);
    }
    if (mid <= ql) {
      return prod_node(tr[id].rs, mid, r, ql, qr, mul, add);
    }
    return prod_node(tr[id].ls, l, mid, ql, qr, mul, add) +
           prod_node(tr[id].rs, mid, r, ql, qr, mul, add);
  }
};

} // namespace noya

 // NOYA_PERSISTENT_AFFINE_SEGTREE_HPP