Editorial for Wesley's Anger Contest 4 Problem 5 - Time Travelling Squirrels
Submitting an official solution before solving the problem yourself is a bannable offence.
Author:
Subtask 1
For the first subtask, we can create a copy of the graph and the value of each of the nodes for each query. We can perform the second and third actions by performing a breadth first search or depth first search from city .
Time Complexity:
Memory Complexity:
Subtask 2
For the second subtask, we only need to keep track of one version of the graph at a time, and do not have to worry about type actions. We can think of the first action as merging two sets together. If we use a data structure such as a priority queue, we can then query for the maximum value in each set in time. To merge sets together efficiently, we can use small to large merging where all elements in the smaller set are merged into the larger set for an amortized time complexity for each merge. Alternatively, one can use pairing heaps or leftist heaps for a worst case time complexity of , randomized heaps for an expected time complexity of , or skew heaps for an amortized time complexity of . Finally, to determine which set a node is currently in, we can use the Union-Find data structure to find the root of each set in amortized time if both union find by size (or rank) and path compression is used.
Time Complexity: if small to large merging is used, if mergeable heaps are used
Memory Complexity:
Subtask 3
For the third subtask, we can handle type actions by maintaining an offset for each heap, representing the amount every element in that heap has been incremented. We will have to add this offset when accessing or removing a value inside the heap, and subtract the offset when inserting a value into the heap. Alternatively, if mergeable heaps are used, we can store a lazy value in each node, and lazily propagate that value down as we access the nodes.
Time Complexity: if small to large merging is used, if mergeable heaps are used
Memory Complexity:
Subtask 4
For the fourth subtask, we will need to quickly store and retrieve previous versions of the heaps and Union-Find data structures. For the array representing the Union-Find data structure, we can use a persistent array. The persistent array can be represented with a balanced binary search tree with nodes. Every time a node is accessed, a new node is created and returned to its parent. We can store the root node of this tree for each version of the data structure. Note that the amortized time complexity of path compression does not apply as there are cases where the same long path is compressed over multiple versions of the data structure and can increase the memory required (thank you for pointing this out). Each find operation takes time.
We can also make the heap persistent by creating and returning new nodes to its parent every time a node is accessed. Note that skew heaps cannot be used as amortization does not apply, and the worst case time complexity is per operation (though data may not have been strong enough to stop all variants of skew heaps). We can then use a persistent array to keep track of the root nodes of each set. Note that nodes could be created in each operation of the persistent heap.
Time Complexity:
Memory Complexity:
Subtask 5
For the full solution, we can combine both the persistent data structures in subtask and the lazy propagation in subtask .
Time Complexity:
Memory Complexity:
Comments