guest@itl:~/home/data-structures$
templates/segment-tree.cpp
compilable
$cat templates/segment-tree

Segment Tree

Classic segment tree with point update and range query for any associative operation.

#segment-tree#range-query#point-update
Log in to track progress, save custom versions, and organize into collections.Log In
$cat source_code/
cpp
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
#define sz(x) int(x.size())

template <typename T = int, typename Op = plus<T>, int Base = 0, typename numsType = T>
class Segment_Tree {
 private:
  int n, max_level;
  T DEFAULT;
  vector<T> tree;
  Op operation;

  void build(const vector<numsType>& nums, int idx, int lx, int rx) {
    if (Base ? lx >= int(nums.size()) : lx > int(nums.size())) return;
    if (rx == lx)
      tree[idx] = T(nums[lx - !Base]);
    else {
      int mx = (rx + lx) / 2;
      build(nums, idx * 2, lx, mx);
      build(nums, idx * 2 + 1, mx + 1, rx);
      tree[idx] = operation(tree[idx * 2], tree[idx * 2 + 1]);
    }
  }

  void update(int index, numsType value, int idx, int lx, int rx) {
    if (rx == lx)
      tree[idx] = T(value);
    else {
      int mx = (rx + lx) / 2;
      if (index <= mx)
        update(index, value, idx * 2, lx, mx);
      else
        update(index, value, idx * 2 + 1, mx + 1, rx);
      tree[idx] = operation(tree[idx * 2], tree[idx * 2 + 1]);
    }
  }

  T query(int l, int r, int idx, int lx, int rx) const {
    if (lx > r || l > rx) return DEFAULT;
    if (lx >= l && rx <= r) return tree[idx];
    int mx = (lx + rx) / 2;
    return operation(query(l, r, idx * 2, lx, mx),
                     query(l, r, idx * 2 + 1, mx + 1, rx));
  }

 public:
  Segment_Tree(int n = 0, const vector<numsType>& nums = vector<numsType>(),
               Op op = Op{}, T def = T{})
      : n(n), max_level(1), DEFAULT(def), operation(op) {
    while ((1 << max_level) < n) max_level++;
    tree = vector<T>(4 * n, DEFAULT);
    if (!nums.empty()) build(nums, 1, 1, n);
  }

  // Rebuild from array — resets tree to DEFAULT first to avoid stale values
  void build(const vector<numsType>& nums) {
    std::fill(tree.begin(), tree.end(), DEFAULT);
    build(nums, 1, 1, n);
  }

  void update(int index, numsType value) { update(index, value, 1, 1, n); }

  T query(int l, int r) const { return query(l, r, 1, 1, n); }

  T operator[](int index) const { return query(index, index, 1, 1, n); }

  int size() const { return n; }

  void print() const {
    if (int(tree.size()) <= 1) return;
    int level = 0;
    queue<pair<int, int>> q;
    q.push({1, level});
    while (!q.empty()) {
      int nodesAtCurrentLevel = q.size();
      int spacesBetween = (1 << (max_level - level + 1)) - 1;
      int leadingSpaces = (1 << (max_level - level)) - 1;
      cout << string(leadingSpaces * 2, ' ');
      while (nodesAtCurrentLevel--) {
        auto [idx, lvl] = q.front();
        q.pop();
        cout << setw(2) << tree[idx];
        if (nodesAtCurrentLevel) cout << string(spacesBetween * 2, ' ');
        if (idx * 2 + 1 < int(tree.size())) {
          q.push({idx * 2, lvl + 1});
          q.push({idx * 2 + 1, lvl + 1});
        }
      }
      cout << "\n";
      level++;
    }
  }
};

void Solve() {
  // write your code here
}

int main() {
  ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
  int t = 1;
  cin >> t;
  while (t--) Solve();
  return 0;
}
107 linesutf-8
$cat explanation_notes.md
notes_viewer --renderedmarkdown (math enabled)

Segment Tree — Generic

Quick Start (ECPC)

Sum segment tree, 0-indexed input:

void Solve()
{
int n; cin >> n;
vector arr(n);
for (auto &x : arr) cin >> x;

Segment_Tree<> seg(n, arr); // sum, 0-indexed

seg.update(3, 10); // set index 3 to 10
cout << seg.query(1, 5) << "\n"; // sum [1..5]
cout << seg[3] << "\n"; // single element
}

Max Segment Tree

struct MaxOp { int operator()(int a, int b) const { return max(a, b); } };

void Solve()
{
int n; cin >> n;
vector arr(n);
for (auto &x : arr) cin >> x;

Segment_Tree seg(n, arr, MaxOp{}, INT_MIN);
cout << seg.query(1, n) << "\n"; // max in [1..n]
}

Template Params

  • T — node/answer type (default int)

  • Op — binary combine functor (default plus)

  • Base0 = 0-indexed, 1 = 1-indexed

  • numsType — input array type (default T)
  • Constructor

    Segment_Tree seg(n, nums, op, def);
    // n — array size
    // nums — initial values (optional)
    // op — combine function (optional)
    // def — identity: 0 for sum, INT_MIN for max, INT_MAX for min, 0 for XOR

    Methods

  • build(nums) — rebuild from array

  • update(i, val) — point update

  • query(l, r) — range query

  • seg[i] — single element

  • size() — array size

  • print() — debug print tree
  • Notes

  • Tree API is always 1-indexed internally, regardless of Base.

  • Base=0: seg.query(1, 5) queries indices 1..5 of your 0-indexed array.

  • Base=1: seg.query(1, 5) queries indices 1..5 of your 1-indexed array.