Dynamic Tree Vertex Add Subtree Sum¶
- Title:
Dynamic Tree Vertex Add Subtree Sum - Judge / source:
Library Checker - Original URL: https://judge.yosupo.jp/problem/dynamic_tree_vertex_add_subtree_sum
- Secondary topics:
Dynamic trees,Treap / split-merge,Subtree aggregates - Difficulty:
very hard - Subtype:
Euler tour tree + point add + edge-oriented subtree sum - Status:
solved - Solution file: dynamictreevertexaddsubtreesum.cpp
Why Practice This¶
This is the cleanest verifier-style rep for the repo's first Euler tour tree lane.
The statement gives:
- one tree with vertex weights
- online
cutandlink - point add on one vertex
- queries of the form:
on edge (v, p), treat p as the parent and ask for the subtree sum of v
That is exactly the first ETT worldview:
- dynamic topology
- subtree-side query
- sequence split/merge
not dynamic path aggregates.
Recognition Cue¶
Reach for Euler tour tree when:
- the tree changes online
- the live query is still "one rooted subtree" or "one side of one edge"
- the statement itself tells you the parent for the query edge
- you can almost see a subtree interval, but a fixed DFS order no longer survives the updates
The smell here is:
the query is subtree-shaped, but the tree is no longer static.
Problem-Specific Transformation¶
Represent the current rooted tree by one sequence of:
- self-loops
(u, u) - directed edge tokens
(u, v)and(v, u)
Store a_u only on (u, u).
Then:
- after
make_root(p), the subtree of adjacent childvis exactly the interval from(p, v)to(v, p) type 1is just adding to self-loop(p, p)type 0is:- reroot one endpoint of the removed edge
- delete the two boundary tokens for that edge
- reroot the new child tree
- splice it under the new parent
So the problem becomes:
maintain one dynamic split/merge sequence with interval sums
instead of maintaining parent arrays or rebuilding DFS orders.
Core Idea¶
The implementation keeps one implicit-treap-style sequence per tree.
Each sequence node is one token:
- self-loop
(u, u)with weighta_u - directed edge token with weight
0
The key invariants are:
- rerooting at
uis rotating the sequence so(u, u)becomes first - if
pis the parent of adjacentv, then the whole subtree ofvis the contiguous interval(p, v) ... (v, p) - cutting edge
{u, v}aftermake_root(u)removes the interval boundary tokens(u, v)and(v, u) - linking child tree
wunder parentxmeans inserting: (x, w)- the whole rerooted sequence of
w (w, x)immediately after(x, x)
That is why ETT is the right fit:
dynamic subtree queries are still interval queries, but the interval order itself must be dynamic.
Implementation Plan¶
- create one self-loop token
(u, u)for every vertex - build the initial tree by linking edges in any acyclic order
- keep a pointer to:
- self-loop token of every vertex
- both directed-edge tokens of every current edge
- answer queries by split/merge around those token pointers
The implicit treap only needs:
- subtree size
- subtree sum
- parent pointers for
index(node)recovery
No lazy propagation is needed for this first route.
Complexity¶
- each
make_root,link,cut, or subtree query uses a constant number of split/merge/rank operations - expected complexity per query:
O(log n) - memory:
O(n + q)tokens
This matches the official 2e5 limits.
Main Traps¶
- trying to solve it with one static DFS flattening
- treating it like link-cut tree path queries instead of subtree-side interval queries
- storing vertex weight on both entry and exit style tokens
- forgetting that query
2 v pgives an edge(v, p)and explicitly sayspis the parent - cutting an edge without rerooting one endpoint first
Reopen Path¶
- Topic page: Euler Tour Tree
- Practice ladder: Euler Tour Tree ladder
- Starter template: euler-tour-tree-subtree-sum.cpp
- Notebook refresher: Euler Tour Tree hot sheet
- Compare points: Euler Tour / Subtree Queries, Link-Cut Tree, Treap / Implicit Treap