Implementations

C++

#include <concepts>
#include <cstddef>
#include <vector>
 
template <typename Ops, typename S, typename L>
concept LazySegConcept = requires(S a, S b, L f, L g, std::size_t len) {
	{ Ops::identity_value() } -> std::same_as<S>;
	{ Ops::combine(a, b) } -> std::same_as<S>;
	{ Ops::identity_lazy() } -> std::same_as<L>;
	{ Ops::compose(f, g) } -> std::same_as<L>;
	{ Ops::apply(f, a, len) } -> std::same_as<S>;
	{
		f == g
	}; // not really needed, just a minor checkf for where I check if no lazy
};
 
template <typename S, typename L, typename Ops>
    requires LazySegConcept<Ops, S, L>
class SegTree {
  private:
	using sz = std::size_t;
	sz n_ = 0;
	std::vector<S> tree_;
	std::vector<L> lazy_;
 
	inline sz left(sz node) { return node << 1; }
 
	inline sz right(sz node) { return (node << 1) + 1; }
 
	// build : O(n)
	void build(sz node, sz l, sz r, const std::vector<S> &values) {
		// [l,r) like range
		if (r - l == 1) {
			tree_[node] = values[l];
			return;
		}
 
		sz m = (l + r) >> 1;
		build(left(node), l, m, values);
		build(right(node), m, r, values);
		pull(node);
	}
 
	// helpers - pull, apply_lazy, push
	void pull(sz node) {
		tree_[node] = Ops::combine(tree_[left(node)], tree_[right(node)]);
	}
 
	void apply_lazy(L lazy, sz node, sz len) {
		// can swap these 2 lines
		// apply lazy s.t. it's green again
		tree_[node] = Ops::apply(lazy, tree_[node], len);
		lazy_[node] = Ops::compose(lazy, lazy_[node]);
	}
 
	void push(sz node, sz len) {
		if (lazy_[node] == Ops::identity_lazy() || len == 1)
			return;
 
		sz half = len >> 1;
		// make children green
		apply_lazy(lazy_[node], left(node), half);
		apply_lazy(lazy_[node], right(node), half);
		lazy_[node] = Ops::identity_lazy();
	}
 
	void set(sz node, sz l, sz r, sz i, const S &value) {
		if (r - l == 1) {
			tree_[node] = value;
			lazy_[node] = Ops::identity_lazy();
			return;
		}
 
		push(node, r - l);
		sz m = (l + r) >> 1;
		if (i < m)
			set(left(node), l, m, i, value);
		else
			set(right(node), m, r, i, value);
		pull(node);
	}
 
	S get(sz node, sz l, sz r, sz i) {
		if (r - l == 1) {
			return tree_[node];
		}
 
		push(node, r - l);
		sz m = (r + l) >> 1;
		if (i < m)
			return get(left(node), l, m, i);
		return get(right(node), m, r, i);
	}
 
	S query(sz node, sz l, sz r, sz ql, sz qr) {
		if (qr <= l || r <= ql)
			return Ops::identity_value();
		if (ql <= l && r <= qr)
			return tree_[node]; // what I enter into is ALWAYS green
 
		push(node, r - l);
		sz m = (l + r) >> 1;
		return Ops::combine(query(left(node), l, m, ql, qr),
		                    query(right(node), m, r, ql, qr));
	}
 
	void apply(sz node, sz l, sz r, sz ql, sz qr, const L &lazy) {
		if (qr <= l || r <= ql)
			return;
		if (ql <= l && r <= qr) {
			apply_lazy(lazy, node, r - l);
			return;
		}
 
		push(node, r - l); // make children green
		sz m = (l + r) >> 1;
		apply(left(node), l, m, ql, qr, lazy);
		apply(right(node), m, r, ql, qr, lazy);
		pull(node);
	}
 
  public:
	// a type alias is not a distinct type
	// this'll work with std:size_t args
	explicit SegTree(sz n)
	    : n_(n), tree_(4 * n + 1, Ops::identity_value()),
	      lazy_(4 * n + 1, Ops::identity_lazy()) {}
 
	explicit SegTree(const std::vector<S> &values) : SegTree(values.size()) {
		if (n_ > 0)
			build(1, 0, n_, values);
	}
 
	void set(sz i, const S &value) { set(1, 0, n_, i, value); }
 
	S get(sz i) { return get(1, 0, n_, i); }
 
	S query(sz l, sz r) { return query(1, 0, n_, l, r); }
 
	void apply(sz l, sz r, const L &lazy) { apply(1, 0, n_, l, r, lazy); }
};
 
struct RangeAddSum {
	static long long identity_value() { return 0; }
	static long long combine(long long a, long long b) { return a + b; }
	static long long identity_lazy() { return 0; }
	static long long compose(long long newer, long long older) {
		return newer + older;
	}
	static long long apply(long long lazy, long long value, std::size_t len) {
		return value + lazy * static_cast<long long>(len);
	}
};
 
int main() {
	SegTree<long long, long long, RangeAddSum> st({1, 2, 3, 4});
	st.apply(1, 3, 5);
	return static_cast<int>(st.query(0, 4));
}

Java

import java.util.*;
 
interface LazySegOps<S, L> {
    S identityValue();
 
    L identityLazy();
 
    S combine(S a, S b);
 
    L compose(L f, L g);
 
    S apply(L f, S a, int len);
}
 
final class SegTree<S, L> {
    private final LazySegOps<S, L> ops;
    private int n;
    private ArrayList<S> tree;
    private ArrayList<L> lazy;
 
    public SegTree(LazySegOps<S, L> ops, int n) {
        this.ops = ops;
        // NOTE: remember the 4n+1
        tree = new ArrayList<>(Collections.nCopies(4 * n + 1, ops.identityValue()));
        lazy = new ArrayList<>(Collections.nCopies(4 * n + 1, ops.identityLazy()));
        this.n = n;
    }
 
    private void build(int node, int l, int r, List<S> list) {
        if (r - l == 1) {
            tree.set(node, list.get(l));
            return;
        }
 
        int m = (l + r) >> 1;
        build(left(node), l, m, list);
        build(right(node), m, r, list);
        pull(node);
    }
 
    public void build(List<S> list) {
        build(1, 0, n, list);
    }
 
    // helpers
    private int left(int node) {
        return node << 1;
    }
 
    private int right(int node) {
        return (node << 1) + 1;
    }
 
    private void pull(int node) {
        tree.set(node, ops.combine(tree.get(left(node)), tree.get(right(node))));
    }
 
    private void applyLazy(L lazyVal, int node, int len) {
        tree.set(node, ops.apply(lazyVal, tree.get(node), len));
        lazy.set(node, ops.compose(lazyVal, lazy.get(node)));
    }
 
    private void push(int node, int len) {
        // NOTE: remember to use the .equals here and not ==
        // also note that this assumes lazy cannot be null since then this equal may
        // throw
        if (len == 1 || lazy.get(node).equals(ops.identityLazy()))
            return;
 
        int half = len >> 1;
        applyLazy(lazy.get(node), left(node), half);
        applyLazy(lazy.get(node), right(node), half);
        lazy.set(node, ops.identityLazy());
    }
 
    // actual funcs
    private S get(int node, int l, int r, int i) {
        if (r - l == 1) {
            return tree.get(node);
        }
 
        int m = (l + r) >> 1;
        push(node, r - l);
        if (i < m)
            return get(left(node), l, m, i);
        return get(right(node), m, r, i);
    }
 
    private void set(int node, int l, int r, int i, S val) {
        if (r - l == 1) {
            // it is an actual hardcoded SET
            tree.set(node, val);
            lazy.set(node, ops.identityLazy());
            return;
        }
 
        push(node, r - l);
        int m = (l + r) >> 1;
        if (i < m)
            set(left(node), l, m, i, val);
        else
            set(right(node), m, r, i, val);
        pull(node);
    }
 
    private S query(int node, int l, int r, int ql, int qr) {
        // REM: THIS IS WHAT'S NATURAL TO ME
        if (qr <= l || r <= ql)
            return ops.identityValue();
        if (ql <= l && r <= qr)
            return tree.get(node);
 
        push(node, r - l);
        int m = (l + r) >> 1;
        return ops.combine(query(left(node), l, m, ql, qr), query(right(node), m, r, ql, qr));
    }
 
    private void apply(int node, int l, int r, int ql, int qr, L lazyVal) {
        if (qr <= l || r <= ql)
            return;
        if (ql <= l && r <= qr) {
            applyLazy(lazyVal, node, r - l);
            return;
        }
 
        push(node, r - l);
        int m = (l + r) >> 1;
        apply(left(node), l, m, ql, qr, lazyVal);
        apply(right(node), m, r, ql, qr, lazyVal);
        pull(node);
    }
 
    public S get(int i) {
        return get(1, 0, n, i);
    }
 
    public void set(int i, S val) {
        set(1, 0, n, i, val);
    }
 
    public S query(int l, int r) {
        return query(1, 0, n, l, r);
    }
 
    public void apply(int l, int r, L lazyVal) {
        apply(1, 0, n, l, r, lazyVal);
    }
}

Notes

core

this is in handwritten notes ( I need to digitise them )

cpp