Editorial for CEOI '09 P2 - Harbingers
Submitting an official solution before solving the problem yourself is a bannable offence.
A quadratic solution. The first approach is to use dynamic programming. If we consider to be the minimum time required to send a message from town to the capital, then we have the following recursion:
where is a node on the path from town to town . This relation is obtained by considering every harbinger that may receive the message from harbinger . By we denote the distance between nodes and . This distance can be computed in constant time if we initially compute a vector , where is the distance from town to the capital. A direct implementation of (1) takes time and earns 20 points.
The special case of lines. Recurrence (1) can be rewritten as:
When computing the value for , is constant for all , so:
We need to find the minimum of expressions of the form . This has a useful geometric interpretation: for each node , we consider a line in the plane given by the equation .
Finding the minimum is now equivalent to vertical ray shooting: find the lowest line in the plane intersected by the line. To solve this query efficiently, we maintain the lower envelope of the lines. Because the road lengths are positive, we have that for every and , where is an ancestor of . This means that the slopes of the lines in the envelope are descending. Thus, we can store the lines in a stack.
At each step , we must:
- query which line in the envelope produces the minimum for .
- insert in the envelope (possibly deleting others).
The first step can be solved efficiently using binary search, since the intersections of with lines in the envelope are a unimodal function.
The second step can be solved in , if lines are deleted from the envelope. We simply pop the head of the stack as long as the newly inserted line completely shadows the segment at the head of the stack. Since every line in the stack is popped from the stack at most once, the overall complexity of this step is . Thus, the entire algorithm runs in time.
The general case. To handle arbitrary trees, we implement the dynamic program by depth-first search. To maintain the lower envelope during DFS, we have to support two data structure operations:
- Insert a line efficiently (when moving down to a child).
- Delete a line and restore the envelope to the previous state (when moving up to a parent).
Point 1. cannot proceed as for line graphs, since a line can be deleted multiple times. Quadratic behavior is obtained if a node has many children and the is large between the node and each child. To insert a line in time, we can once more use binary search on a unimodal function. (Solutions that do linear insertion here earn 70 points.)
For the undo operations, we maintain the lower envelope in a global array. To delete lines, we simply store the new line positions back from the top of the stack, and leave the other positions intact. We also save the line that was overwritten by the newly inserted line. To restore the stack, we simply reset its top pointer, and restore the saved line to its original location in the stack. The overall complexity is .
An alternative visualization of the algorithm does not use lower envelopes, but linear programming; we maintain the convex hull, and use unimodal search to find the optimum.
Though the reasoning behind the algorithm is quite complicated, the code is fairly short (~40 lines).
Comments