Many dynamic segment trees over the same value range share one node pool. Two trees can be merged (union of multisets) and a tree can be split by value or by count. All merges together cost , which is . That makes this the tool for subtree aggregates on trees (an alternative to small-to-large), for “sort a range” operations, and for set unions in DSU-like processes.

Implementation

struct MergeSeg {
    int n; vector<int> L{0}, R{0}; vector<ll> c{0};     // node 0 = empty tree, never modified
    MergeSeg(int n) : n(n) {}
    int node() { L.push_back(0); R.push_back(0); c.push_back(0); return sz(c) - 1; }
    int add(int t, int v, ll d, int l, int r) {          // returns the (possibly new) root
        if (!t) t = node();
        c[t] += d;
        if (r - l > 1) {
            int m = (l + r) / 2;
            if (v < m) { int x = add(L[t], v, d, l, m); L[t] = x; }
            else { int x = add(R[t], v, d, m, r); R[t] = x; }
        }
        return t;
    }
    int add(int t, int v, ll d) { return add(t, v, d, 0, n); }
    int merge(int a, int b, int l, int r) {              // destroys b, reuses a
        if (!a || !b) return a ^ b;
        if (r - l == 1) { c[a] += c[b]; return a; }
        int m = (l + r) / 2;
        int x = merge(L[a], L[b], l, m), y = merge(R[a], R[b], m, r);
        L[a] = x; R[a] = y; c[a] = c[x] + c[y];
        return a;
    }
    int merge(int a, int b) { return merge(a, b, 0, n); }
    pair<int, int> splitCount(int t, ll k, int l, int r) {   // (k smallest, rest)
        if (!t || k <= 0) return {0, t};
        if (k >= c[t]) return {t, 0};
        int u = node();
        if (r - l == 1) { c[u] = c[t] - k; c[t] = k; return {t, u}; }
        int m = (l + r) / 2; ll left = c[L[t]];
        if (k <= left) { auto [a, b] = splitCount(L[t], k, l, m); L[t] = a; L[u] = b; R[u] = R[t]; R[t] = 0; }
        else { auto [a, b] = splitCount(R[t], k - left, m, r); R[t] = a; R[u] = b; }
        c[t] = c[L[t]] + c[R[t]]; c[u] = c[L[u]] + c[R[u]];
        return {t, u};
    }
    int kth(int t, ll k) const {                        // 0-indexed, assumes k < c[t]
        int l = 0, r = n;
        while (r - l > 1) { int m = (l + r) / 2; if (k < c[L[t]]) t = L[t], r = m; else k -= c[L[t]], t = R[t], l = m; }
        return l;
    }
};

Take returned child indices into locals before assigning (push_back may reallocate the vectors). Split by value is the same recursion as splitCount, comparing with the midpoint instead of with the left count. Never use a tree after it has been merged into another, and never merge a tree with itself.

Uses

Subtree queries on a tree: give each vertex a tree over its values (colours, depths, ranks), process vertices bottom-up with root[v] = merge(root[v], root[child]), and answer ‘s queries right after its children are merged in, before is merged into its parent (later merges reuse its nodes). For “most frequent colour in each subtree” (CF 600E), store the max count and the sum of colours achieving it in each node, combining at the leaves of merge. total, often simpler than small-to-large.

Depth-indexed tree DP: “for each , the number of descendants at each depth” merges trees indexed by depth. When you need the whole array per vertex, long-path decomposition is instead (Long-Path Decomposition).

DSU with sets of values: merge the value trees when components merge; component queries like “-th smallest in my component” become kth.

Range sort (sortable array): keep the array as runs, each run a value-tree (or binary trie over distinct keys) plus a direction flag; an outer structure (set of run starts, segment tree of run aggregates) folds runs. Sorting splits the boundary runs by count and merges all runs inside. By the merge bound the total is amortized. With per-node folds in both key orders (forward and backward), this supports “point set, range sort, range composite” (Library Checker) with non-commutative monoids. The trie needs distinct keys to index by; break ties with pairs when the values repeat.

Compared with small-to-large

Segment tree mergingSmall-to-large with map
time total
memory nodes
supportsany segment-tree aggregate over values, split, -thanything a container supports
order of queriesanswer before merging upwardsame