Solution 1: Euler Tour + Binary Lifting
Explanation
This problem requires supporting point updates and XOR path queries on a tree. We can perform an Euler Tour combined with a Fenwick tree to compute root-to-node XORs efficiently and binary lifting to find the Lowest Common Ancestor (LCA) of any two nodes.
Let and denote the entry and exit times of node during an Euler Tour. By storing each node's value at both positions and in the Fenwick tree, the prefix XOR up to gives the XOR of all values on the path from the root to node .
This works because nodes in the subtree of have entry times in the range , so their root paths all contain . We XOR at to include it for the subtree, and XOR it again at to cancel it out for nodes outside the subtree. This is possible due to XOR's self-inverse property.
Define as the XOR of values from the root to node . Then the XOR along the path between nodes and is:
and each include the path from the root to their LCA, which cancels out when XORed together. We must XOR in once to include the LCA node in the final result.
To update a node to a new value , we XOR the difference into both and .
Implementation
Time Complexity:
C++
#include <bits/stdc++.h>#define MAXN 100005#define bitinc(x) (x & -x)using namespace std;int n, arr[MAXN], bit[2 * MAXN + 5], in[MAXN], ot[MAXN], par[MAXN][22];vector<int> adj[MAXN];int timer = 1;
Java
import java.io.*;import java.util.*;public class CowLand {static final int LOG = 18;Code Snippet: Binary Indexed Tree (Click to expand)static ArrayList<Integer>[] adj;static int[] in, outTime, a;
Solution 2: Heavy-Light Decomposition
Explanation
We can also solve this using HLD. After decomposing the tree into heavy paths, we assign each node a position in a linearized array and store values at . Each heavy path is contiguous in this array, with storing the top of the heavy chain containing .
For a path query between nodes and , we repeatedly jump from the deeper node to the parent of its chain top until both nodes are on the same chain. At each step, we need to query the XOR of a contiguous segment of the linearized array. To achieve complexity, we must use a data structure such as a segment tree (or Fenwick tree) to answer these segment queries in .
Implementation
Time Complexity:
C++
#include <algorithm>#include <cstdio>#include <iostream>#include <vector>using namespace std;const int MAXN = 100005;int n, q;
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!