Store a tree’s Euler tour in a balanced BST, so that link and cut become sequence split and merge operations in .

The representation

The Euler tour of a tree with vertices is a sequence of entries (each edge traversed twice, plus the vertices). Represent it as a balanced sequence — a treap or splay tree keyed implicitly by position.

Cut edge where is the child: the subtree of occupies a contiguous block of the tour. Split it out; that block is the new tree, and merging the two remaining pieces gives the old tree minus the subtree.

Link to : re-root ‘s tour so is first (a cyclic rotation — two splits and a merge), then splice that tour into ‘s tour just after ‘s occurrence.

Both are : a constant number of splits and merges.

What it supports

OperationCost
link, cut
connected(u, v) — same BST root
Subtree aggregate (sum, size, min)
Subtree update with lazy tags
Point update
Path aggregatenot supported
Euler tour treeLink-cut tree
Subtree querieshard (needs virtual subtrees)
Path queries
Rooted-ness mattersno (unrooted)yes (evert re-roots)
Implementationmoderate (sequence BST)hard
Used insideHDT dynamic connectivityflow speedups, dynamic MST

They are complementary: paths versus subtrees. Neither subsumes the other.

Where they matter

Euler tour trees are the engine inside HDT dynamic connectivity: the algorithm maintains nested spanning forests, each stored as Euler tour trees, and needs exactly the operations ETTs provide — link, cut, and “find any incident non-tree edge in this subtree”.

Outside that, they appear when a problem asks for subtree sums on a changing tree, which HLD and plain Euler tours cannot handle because the tour order itself changes.

In a contest

Almost never. If the tree is static, a plain Euler tour plus a BIT gives subtree queries in with fifteen lines of code. If edges change and everything is offline, the segment-tree-on-time technique is simpler. Euler tour trees are for the genuinely online, genuinely dynamic case.

Implementation sketch

// treap over the Euler tour; each node carries a vertex id and an aggregate
Node* reroot(Node* t, int v) {
    Node *L, *R;
    split(t, posOf(v), L, R);       // rotate so v's first occurrence leads
    return merge(R, L);
}
void cut(int u, int v) {            // v is the child
    Node *A, *B, *C;
    split(root, tin[v], A, B);
    split(B, tout[v] - tin[v], B, C);
    root = merge(A, C);             // B is now v's subtree, a separate tree
}

Maintaining posOf under splits and merges requires storing parent pointers and walking up to compute a node’s index — an operation in a treap.

See also: Euler Tour · Link-Cut Tree · Dynamic Connectivity