Editorial for Yet Another Contest 1 P6 - Alchemy
Submitting an official solution before solving the problem yourself is a bannable offence.
Author:
Subtask 1
We can model each element as a node in the graph, with a spell being modelled as a weighted bidirectional edge in the graph. The minimum total cost corresponds to the shortest path in the graph from node to node . This can be found easily using Dijkstra's algorithm.
Subtask 2
First, let's deal with the additional fees.
Instead of a node representing an element in the graph, each node will have an additional state - the current alchemist we are visiting (or none if we are not currently visiting an alchemist). From now on, let us refer to a node as , where represents the current element and represents the current alchemist we are visiting (or if we are not visiting any alchemist). Edges from type 2 alchemists now connect the nodes and . The answer to the problem is the shortest distance from to .
To handle the transition between visiting different alchemists, for each alchemist , and for each element they work with , we will add a directed edge from to with a weight of 0, and a directed edge from to with weight . This works because switching from visiting alchemist to alchemist whilst having element can be represented by traversing the path , with the weight of the path being .
Now, how do we deal with type 1 alchemists?
If we add edges to the graph naively, then there will be edges in the graph, which is too much.
Instead, we will use the same idea as for handling the additional fees. For each type 1 alchemist , we will create a separate auxiliary node . For each element that the alchemist works with, we will add an undirected edge between to with a weight of . This works because transforming element to is equivalent to traversing the path , with the weight of the path being .
Subtask 3
Unfortunately, having the base cost of a visit being the maximum of the values of the spells performed doesn't lend itself as easily to Dijkstra's algorithm. We need to find a way to convert the costs of paths being the maximum of edge values to addition of edge values in order for Dijkstra's algorithm to work.
For now, let us focus on a different, general graph with nodes, where the weight of a path is maximum of the weight of its edges. How can we make this graph Dijkstra-able?
Let's consider the minimum weight path from node to (assuming they are in the same component). We can find this path by adding edges to the graph in increasing order by weight, until there is a path from to . Observe that we are essentially performing Kruskal's algorithm to find the minimum spanning tree - in fact, the minimum weight path between any two nodes in the same component is simply the path between them on the minimum spanning tree (technically a minimum spanning forest).
Unfortunately, this graph is still not Dijkstra-able. Currently, in Kruskal's algorithm, when we process an edge between two different components with leaders and , we add an edge between and in our union-find data structure. However, instead, we could create a new auxiliary node , and set the leaders of the components we are merging to .
How does this representation help us? To see this, assign each of the initial nodes in the graph a value of 0. For each edge in ascending order by weight, if it connects two different components, then after creating the auxiliary node , we will assign a value equal to the weight of the current edge we are processing in the original graph. We add directed edges from to , from to , from to , and from to . Let us call the edges from to and from to 'up edges', and the edges from to and from to 'down edges'. We then set the leaders of the two components we are merging to node in our union-find data structure.
After this process is completed, for each component in the original graph, we will have created a tree where the leaves are the nodes in the original graph. (In fact, this tree is known as a Kruskal Reconstruction Tree.) Consider the path from node to node in the original graph. Let be the lowest common ancestor of and in the tree. Then is an auxiliary vertex which represents the final edge which connected the components and were in during Kruskal's algorithm, so the value of is equal to the minimum weight path between and .
For all up edges from to , we will assign a weight equal to the value of , minus the value of . All down edges will have a weight of 0.
Now, for any two leaf nodes and in the tree with lowest common ancestor , the value of the path between and in the tree will be exactly equal to the value of , due to a telescoping sum (and the fact that node has a value of 0). Furthermore, since Kruskal's algorithm processes edges in increasing order, for all up edges from to , the value of will be greater than or equal to the value of , so all edges in our resulting tree will be non-negative. This means that we can use Dijkstra's algorithm on the tree.
This algorithm is directly applicable for handling type 3 alchemists. For each alchemist, we will simply build the Kruskal Reconstruction Tree as described above (technically it is a Kruskal Reconstruction Forest). Then, our final answer can still be computed via Dijkstra's algorithm.
Comments