Explanation

Let aa, bb, and tt be the nodes given in each query, and pp be the LCA of aa and bb. Directly updating the path from aa to bb is difficult to do, so breaking up the path into smaller segments is necessary. To do that, we use some casework.

Case 1: pp is equal to tt, or tt is not inside pp's subtree.

In this case, we update the path from aa to pp and bb to pp. Note that if pp is equal to either aa or bb, then this case must be handled slightly differently.

Case 2: pp is equal to either aa or bb.

Let the first ancestor of tt that is on the path from aa to bb be ll. WLOG, let bb be equal to pp. Then, we update the path from aa to ll and the path from ll to bb.

Case 3: The LCA of tt with aa and bb is the same as pp.

Like how we handled case 1, for this case we update the path from aa to pp and the path from bb to pp.

Case 4: Either the LCA of tt and aa or the LCA of tt and bb lie on the path from aa to bb.

In this case, split the path updates into 3 segments. Let the lower of the two LCAs mentioned be ll, and the node which is an ancestor of ll be aa. Then, just update the path from aa to ll, ll to pp, and the path from bb to pp.

Handling Path Updates

Note that based on how we did our casework on the paths, that all path updates are between a given node and its ancestor. Also, note that the path updates are just adding distances which either increase or decrease by a fixed amount every time. So, we can sort of do a difference array on a tree, where we DFS down the tree and apply the differences while walking back up the tree.

Implementation

Time Complexity: O(NlogN)\mathcal{O}(N\log{N})

C++

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
class Tree {
private:
const int n;
const int log2dist; // # of bits needed for binary lift
vector<vector<int>> &adj; // reference to adjacency list
vector<vector<int>> lift; // for binary lifting

Join the USACO Forum!

Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!