Flatten the tree in DFS preorder: the subtree of becomes the interval , so every array structure (Fenwick, lazy segment tree, persistent tree, Mo) works on subtrees. Paths are not intervals, but root paths are recovered with entry/exit tricks and LCA (LCA); general path updates and queries go to HLD.

struct EulerTour {                                         // subtree(u) = [tin[u], tout[u]) in preorder
    int n; vector<int> tin, tout, order, par, dep; vector<vector<int>> ch;   // ch[u] in tin order
    EulerTour(const vector<vector<int>>& adj, int root) : n(sz(adj)), tin(n), tout(n), par(n, -1), dep(n), ch(n) {
        vector<int> it(n), st{root}; order = {root};
        while (sz(st)) {
            int u = st.back();
            if (it[u] < sz(adj[u])) {
                int v = adj[u][it[u]++]; if (v == par[u]) continue;
                par[v] = u, dep[v] = dep[u] + 1, tin[v] = sz(order), order.push_back(v), ch[u].push_back(v), st.push_back(v);
            } else tout[u] = sz(order), st.pop_back();
        }
    }
    bool isAnc(int u, int v) const { return tin[u] <= tin[v] && tout[v] <= tout[u]; }   // u ancestor of v (or equal)
    int childToward(int u, int v) const {                  // child of u whose subtree contains v (u a proper ancestor)
        return *prev(upper_bound(all(ch[u]), v, [&](int x, int c) { return tin[x] < tin[c]; }));
    }
};

Reductions

OperationArray form
point update, subtree sumFenwick at tin, range sum (CSES “Subtree Queries”)
subtree add, point valuerange add / point query (difference Fenwick)
subtree add, subtree sum / maxlazy segment tree
root-path sum with point updatessubtree add of at ‘s interval; = point query at
path sum – with (any group: xor, sums mod )
path XOR – — the "" term is XOR-ing twice, which is already free (any value XORed with itself cancels), so only the survives
path add, point querydifference on the tree: at ; at and ; value = subtree sum (edge-weight variant with the weight pushed to the child endpoint: see tree difference array)
path add, subtree sumtwo Fenwicks (below)
entry , exit tour (size )prefix up to = root path; Mo’s on trees (Mo’s Algorithm): map each query to the array range (or plus the LCA, when is not an ancestor of ), then run Mo’s with a toggle instead of add/remove — the first time the pointer crosses a vertex, activate it (e.g. add its colour to a frequency map); the second time (its exit), deactivate it; every vertex off the path is toggled on then off and vanishes, exactly simulating the path
vertices at depth in subtree()per-depth sorted tin lists, two binary searches
-th smallest in a subtreepersistent segment tree over the preorder array
subtree of after rerooting at whole tree, or the complement of the child-toward- interval
distinct colours in a subtreeoffline over intervals (Offline 2D Queries) or small-to-large
struct PathAddSubtreeSum {                                 // add x on path u - v; sum of a subtree
    const EulerTour& T; Fenwick<ll> A, B;                  // A: c(z), B: c(z) * (dep z + 1)
    PathAddSubtreeSum(const EulerTour& T) : T(T), A(T.n), B(T.n) {}
    void point(int z, ll c) { if (z < 0) return; A.add(T.tin[z], c); B.add(T.tin[z], c * (T.dep[z] + 1)); }
    void pathAdd(int u, int v, int l, ll x) { point(u, x); point(v, x); point(l, -x); point(T.par[l], -x); }   // l = lca(u, v)
    ll subtreeSum(int w) const { return B.sum(T.tin[w], T.tout[w]) - (ll)T.dep[w] * A.sum(T.tin[w], T.tout[w]); }
};
vector<pair<int, int>> rerootedSubtree(const EulerTour& T, int u, int r) {   // subtree of u when rooted at r, as intervals
    if (u == r) return {{0, T.n}};
    if (!T.isAnc(u, r)) return {{T.tin[u], T.tout[u]}};
    int c = T.childToward(u, r);
    return {{0, T.tin[c]}, {T.tout[c], T.n}};
}
int countAtDepth(const EulerTour& T, const vector<vector<int>>& byDep, int u, int d) {   // byDep[d]: sorted tins
    if (d >= sz(byDep)) return 0;
    auto& v = byDep[d];
    return int(lower_bound(all(v), T.tout[u]) - lower_bound(all(v), T.tin[u]));
}

The three tours

TourLengthGives
preorder tin/toutsubtree intervals, ancestry test
entry and exit eventsroot-path sums by (exit cancels entry), Mo on trees, bracket encoding of the tree shape
Euler walk (vertex after each child)LCA by depth RMQ, dynamic diameter (Tree Basics), Euler tour trees

Dynamic versions: keep the walk in a balanced BST to support link/cut with subtree aggregates (Euler Tour Tree).

Entry/exit cancellation needs an invertible operation

The tour’s “side branches cancel” trick relies on being able to undo a value (sum, XOR) when exiting a subtree. It does not work for max, min, matrix products, or string concatenation — there is no way to “un-max” a value once a branch has been explored and left. For a path query on a non-invertible operation, abandon this tour and build HLD instead.