Extend Mo’s algorithm to path queries on a tree by flattening the tree into a sequence where every path corresponds to a contiguous range.

The Euler tour with two occurrences

Record each vertex twice — on entry (tin[v]) and on exit (tout[v]) — giving an array of length .

int timer_ = 0;
vector<int> euler(2 * n), tin(n), tout(n);
void dfs(int v, int p) {
    tin[v] = timer_; euler[timer_++] = v;
    for (int u : adj[v]) if (u != p) dfs(u, v);
    tout[v] = timer_; euler[timer_++] = v;
}

Mapping a path to a range

For a query on the path (assume ), let :

CaseRangeExtra
( is an ancestor of )
otherwiseadd the LCA separately

The key rule: a vertex whose occurrences both lie inside the range is not on the path (it is in a side subtree that the path enters and leaves); a vertex appearing exactly once is on the path.

So the add/remove operation toggles:

vector<int> cnt(n, 0);
void toggle(int v) {
    if (cnt[v]) { cnt[v] = 0; removeVertex(v); }
    else        { cnt[v] = 1; addVertex(v); }
}

Each add/remove in the Mo loop becomes a toggle, and the parity handles the “appears twice” case automatically.

The full loop

sort(queries.begin(), queries.end(), moComparator);      // by block of L, then R
int curL = 0, curR = -1;
for (auto& q : queries) {
    while (curR < q.r) toggle(euler[++curR]);
    while (curL > q.l) toggle(euler[--curL]);
    while (curR > q.r) toggle(euler[curR--]);
    while (curL < q.l) toggle(euler[curL++]);
    if (q.lca != -1) toggle(q.lca);                       // include the LCA
    ans[q.id] = currentAnswer;
    if (q.lca != -1) toggle(q.lca);                       // and remove it again
}

Complexity: — the array has length , so the constant is doubled relative to array Mo’s.

What it answers

Anything that Mo’s answers on an array, but along a tree path:

QueryState
Number of distinct values on the patha frequency array + a distinct counter
Most frequent value on the pathfrequency + count-of-counts
Number of pairs with equal valuesmaintain the pair count incrementally
Sum of over valuesincremental
-th smallest on the pathMo’s + a value BIT,

Subtree queries are easier

For subtree (not path) queries, use the ordinary Euler tour with tin/tout — the subtree is a contiguous range and plain array Mo’s applies directly, with no toggling and no LCA correction.

Better still: subtree queries usually have an solution via DSU on tree or a BIT over the Euler tour. Reach for Mo’s only when the aggregate resists both.

Alternatives for path queries

SituationMethodCost
Aggregate is associative, no updatesbinary lifting
Aggregate is associative, with updatesHLD + segment tree
Aggregate needs add/remove semanticsMo’s on trees
Counting over all pathscentroid decomposition
Small vertex subsets per queryvirtual tree

Mo’s on trees is the fallback for aggregates like “number of distinct values” that HLD cannot merge — its niche is real but narrow.

Practical notes

  • Precompute the LCA with binary lifting or Euler+sparse table.
  • Use the Hilbert curve ordering of queries (as in array Mo’s) for a ~30% constant-factor improvement.
  • Watch the memory: the Euler array is , and the frequency array is over the value range (compress it).

See also: Mo’s Algorithm · Euler Tour · DSU on Tree