templates/mos-on-trees.cpp
compilable
$cat templates/mos-on-trees
Mo's Algorithm on Trees
Mo's algorithm adapted for tree path queries using Euler tour flattening and LCA via binary lifting.
#mo-algorithm#tree#euler-tour#lca#offline
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;
const int MAXN = 2e5 + 5;
const int LOG = 18;
vector<int> adj[MAXN];
int S[MAXN], E[MAXN], FT[2 * MAXN], dep[MAXN], anc[MAXN][LOG];
int nodeFreq[MAXN], n, q, timer = 1;
ll ans;
ll answers[MAXN];
void dfs(int u, int p) {
S[u] = timer; FT[timer++] = u;
for (int v : adj[u]) {
if (v == p) continue;
dep[v] = dep[u] + 1;
anc[v][0] = u;
for (int k = 1; k < LOG; k++)
anc[v][k] = anc[anc[v][k-1]][k-1];
dfs(v, u);
}
E[u] = timer; FT[timer++] = u;
}
int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
int diff = dep[u] - dep[v];
for (int k = LOG - 1; k >= 0; k--)
if (diff >> k & 1) u = anc[u][k];
if (u == v) return u;
for (int k = LOG - 1; k >= 0; k--)
if (anc[u][k] != anc[v][k])
u = anc[u][k], v = anc[v][k];
return anc[u][0];
}
int64_t hilbert(int x, int y, int pw, int rot) {
if (!pw) return 0;
int hp = 1 << (pw - 1);
int seg = (x < hp) ? ((y < hp) ? 0 : 3) : ((y < hp) ? 1 : 2);
seg = (seg + rot) & 3;
const int dr[] = {3, 0, 0, 1};
int nx = x & (x ^ hp), ny = y & (y ^ hp);
int64_t sub = int64_t(1) << (2 * pw - 2);
int64_t add = hilbert(nx, ny, pw - 1, (rot + dr[seg]) & 3);
return seg * sub + ((seg == 1 || seg == 2) ? add : sub - add - 1);
}
struct Query {
int l, r, lc, idx;
int64_t ord;
bool operator<(const Query &o) const { return ord < o.ord; }
};
void add(int u) {
// update ans when node u enters range
}
void rem(int u) {
// update ans when node u leaves range
}
void toggle(int pos) {
int u = FT[pos];
nodeFreq[u] ^= 1;
nodeFreq[u] ? add(u) : rem(u);
}
void solve() {
dfs(1, 0);
int pw = 1;
while ((1 << pw) < 2 * n) pw++;
vector<Query> qs(q);
for (int i = 0; i < q; i++) {
int u, v; cin >> u >> v;
int l = lca(u, v);
if (S[u] > S[v]) swap(u, v);
if (l == u) qs[i] = {S[u] + 1, S[v], -1, i, hilbert(S[u] + 1, S[v], pw, 0)};
else qs[i] = {E[u], S[v], l, i, hilbert(E[u], S[v], pw, 0)};
}
sort(qs.begin(), qs.end());
int cl = qs[0].l, cr = qs[0].l - 1;
for (auto &[l, r, lc, qi, _] : qs) {
while (cl > l) toggle(--cl);
while (cr < r) toggle(++cr);
while (cl < l) toggle(cl++);
while (cr > r) toggle(cr--);
if (lc != -1) add(lc);
answers[qi] = ans;
if (lc != -1) rem(lc);
}
for (int i = 0; i < q; i++)
cout << answers[i] << '\n';
}99 linesutf-8
$cat explanation_notes.md
notes_viewer --renderedmarkdown (math enabled)
Mo's on Trees
Answers offline path queries on a tree by flattening the tree into a linear sequence via Euler tour, then applying Mo's algorithm on that sequence.
How It Works
FT[] maps positions to nodes — each node appears twice (entry and exit)- If : query range is
- Otherwise: query range is , and manually add/remove the LCA
nodeFreq), so only nodes on the actual path remain activeWhen to Use
add(node) and remove(node) are or Implementation
add(u) and remove(u) with problem-specific logicVAL_ON_EDGE = true if values live on edges instead of nodes