Skip to content

Breakdown

Published: at 11:07 PM

Here’s a link to the problem.

The first thing we should consider is how to solve the problem without any updates. Naively, we enumerate all possible paths in O(NK2)\mathcal{O}(N^{K - 2}) time. However, note that simply writing K2K - 2 nested loops recomputes a ton of redundant information.

In particular, if, for a node ii, there are xx paths from node 1 to node ii that use aa edges and yy paths from node ii to node nn that use KaK - a edges, our loop takes O(xy)\mathcal{O}(xy) to process all of these paths, when we could instead determine the shortest path in O(x+y)\mathcal{O}(x + y) by picking the shortest path on both sides.

This motivates a divide-and-conquer approach. If, for every node 1in1 \leq i \leq n, we determine the shortest paths from 11 to ii and from ii to nn which each use exactly K/2K / 2 edges (we assume KK is even for convenience), we can determine the shortest path from 11 to nn which uses KK edges in O(n)\mathcal{O}(n) time by enumerating the path’s midpoint.

Thus, we can write the following recurrence relation for time complexity:

T(K)=2NT(K/2)T(K) = 2N \cdot T(K / 2)

where T(K)T(K) denotes the time complexity of finding the shortest path between a fixed pair of nodes s,ts, t which uses exactly KK edges. The factor of 2 arises from the fact that we must find the shortest path both from ss and to tt, and the factor of NN is from enumerating all midpoints.

Solving the recurrence relation with base case T(1)=O(1)T(1) = \mathcal{O}(1), we get T(K)=O(KNlogK)T(K) = \mathcal{O}(K * N^{\log K}), and substituting K=8K = 8, this is O(N3)\mathcal{O}(N^3). So far, so good; but how do we handle updates?

First, as is typical of graph problems which involve deleting edges, we consider reversing the order of operations. Thus, we just need to figure out how to support adding edges, where each edge is added exactly once.

Intuitively, not too much information changes after adding a single edge; we now just have to quantify exactly how much “not too much” is.

Note that our divide-and-conquer structure means that the answer to our problem can change only when the answers to its left/right subproblems change. This crucial property of DNQ is also what allows segment trees to work so quickly. You can thus think of DNQ structures as a method to bundle information in a way such that as much information is preserved between updates as possible.

Going back to our problem, we can write another recurrence relation for the total number of times our states will change:

C(K)=2NC(K/2)U(K)C(K) = 2N\cdot C(K/2) \cdot U(K)

where U(K)U(K) represents the time it takes to process the change of a single substate. Luckily for us, U(K)=1U(K) = 1 for all KK (hopefully you understand why!), and C(1)=1C(1) = 1 as well (since each edge is added exactly once) so in fact, we have

C(K)=T(K)=O(N3)C(K) = T(K) = \mathcal{O}(N^3)

Therefore, our DNQ approach will still run in time, even with updates!


Next Post
About this blog