Codeforces Round #457 (Div. 2) Editorial

Revision en9, by STommydx, 2018-01-20 11:18:08

I would like to take this opportunity to express my deepest apology to all of you who take your own precious time to participate in this unrated round. Also, apologies to vintage_Vlad_Makeev who really helped a lot in preparing the round and MikeMirzayanov who helped to host the round, I did not do a good job in managing the round. As the main author of this round, I'm undoubtedly responsible for the mistake that not writing a brute force solution to test the correctness of the intended solution. It is my responsibility to make sure everything is right before the round starts. I am really sorry that the round must be unrated to ensure fairness to all contestants.

I hope all of you can learn something from the contest. Do not claim a greedy solution absolutely correct (like me :C) unless you have proved it. On the bright side, I'm really glad that some of you found problem D and E interesting as said in some comments in the announcement blog post. I admit that problem D may seem to be a quite standard data structure problem but we think it would be fun for less experienced contestants to think how to put the concepts learnt together.

Just a little unimportant fact: The original div2B problem was a harder data structure problem which involves a slightly complicate mathematical proof (which the current one doesn't have :C). We replaced it because it is too difficult for div2B as the coordinator suggested.

#### 916A - Jamie and Alarm Snooze

idea: STommydx, preparation: southball

Let's use brute force the find the answer. We first set the alarm time as hh: mm and initialize the answer as 0. While the time is not lucky, set the alarm time to x minute before and add 1 to the answer.

Why does this solution run in time? As x ≤ 60, hh decrease at most 1 for every iteration. Also, after at most 60 iterations, hh must decrease at least once. All time representation that hh = 07 (07:XX) is lucky so at most 24 times decrement of hh will lead to a lucky time. Therefore, the max. number of iteration possible is 24 * 60 = 1440 which is very small for 1 sec TL.

In fact, the largest possible answer is 390 where x = 2, hh = 06 and mm = 58.

My implementation: 34342125

#### 916B - Jamie and Binary Sequence (changed after round)

idea: southball, preparation: STommydx

The main idea of the solution is 2x = 2·2x - 1, that means you can replace 1 x element with 2 (x - 1) elements. To start with, express n in binary — powers of two. As we can only increase the number of elements, there is no solution if there exists more than k elements.

Let's fix the y value first. Observe that we can decrease the y value only if all y can be changed to y - 1. So we scan from the largest power and try to break it down if doing so does not produce more than k elements. After y is fixed, we can greedily decrease the smallest element while the number of elements is less than k.

My implementation: 34342011

#### 916C - Jamie and Interesting Graph

idea: STommydx, preparation: STommydx

First, observe that only n - 1 edges are required to fulfil the requirement, so we will make the other m - n + 1 edges with a very large number so they would not contribute to the shortest path or the MST. Now, the problem is reduced to building a tree with prime weight sum and two nodes in the tree have prime distance.

Recall that a path graph is also a tree! If we join (i, i + 1) for all 1 ≤ i < n, the shortest path will lie on the whole tree. We are left with a problem finding n - 1 numbers that sum to a prime. Let's make 1 edge with weight p - n + 2 and others with weight 1. Choosing a prime slightly larger than n (e.g. 100003) will fulfil the requirement for all cases.

My implementation: 34342305

#### 916D - Jamie and To-do List

idea: STommydx, preparation: STommydx

Let's solve a version that does not consist of undo operation first. The task can be divided to two parts: finding the priority of a string and finding the rank of a priority. Both parts can be solved using trie trees. The first part is basic string trie with get and set operation so I will not describe it here in details. The second part is finding a rank of the number which can be supported by a binary trie.

To support the undo operation, observe that each operation only add at most 31 nodes to the trie trees. Therefore, we can make use the idea of persistent data structure and store all versions by reusing old versions of the data structure with pointers. Remember to flush the output after each query operation.

As pointed out by some of you, there exists alternative solutions using persistent dynamic segment trees.

My implementation: 34342389 (sorry for a bit messy)

#### 916E - Jamie and Tree

idea: longhuenchan, preparation: longhuenchan

Let's solve the problem without operation 1 first. That means the subtree of a vertex does not change.

For operation 2, the subtree of smallest size that contains u and v means the lowest common ancestor (lca) of u and v, and we update the subtree of lca. For operation 3, we query the sum of the subtree rooted at the given vertex. To do this, we can flatten a tree into an one dimensional array by considering the DFS order of the vertices starting from vertex 1. If a vertex has DFS order x and its subtree has size y, then the update/query range is [x..x + y - 1]. This can be done by standard data structures, such as binary indexed tree with range update function, or segment tree with lazy propagation.

Things get more complicated when the root of the tree r changes. One should notice that in order to reduce time complexity, we should not recalculate everything when r changes. We just need to keep a variable storing the current root. Now let's discuss the two main problems we face (In the following context, subtree of a vertex is defined according to vertex 1 unless otherwise stated):

How to find the LCA of u and v using the precomputed LCA table that assumes the root is vertex 1? Let's separate the situation into several cases. If both u and v are in the subtree of r, then query the LCA directly is fine. If exactly one of u and v is in the subtree of r, the LCA must be r. If none of u and v is in the subtree of r, we can first find the lowest nodes p and q such that p is an ancestor of both u and r, and q is an ancestor of both v and r. If p and q are different, we choose the deeper one. If they are the same, then we query the LCA directly. You can find p, for example by binary searching on the smallest value n such that the nth ancestor of u is an ancestor of r.

After we have found the origin w of update (for query, it is given), how to identify the subtree of a vertex and carry out updates/queries on it? Again, separate the situation into several cases. If w = r, update/query the whole tree. If w is in the subtree of r, or w isn't an ancestor of r, update/query the subtree of w. Otherwise, update/query the whole tree, then undo update/exclude the results of the subtree of w', such that w' is a child of w and the subtree of w' contains r.

The above ideas can be verified by working with small trees on paper.

longhuenchan's implementation: 34344307

We are glad to see some more elegant implementations by the contestants.

Feel free to discuss the problems below. I am happy to listen to feedback and answer questions from you guys. :)

#### History

Revisions

Rev. Lang. By When Δ Comment
en10 STommydx 2018-01-20 15:42:53 158 Updated E editorial
en9 STommydx 2018-01-20 11:18:08 2604 Added E tutorial
en8 STommydx 2018-01-20 10:17:13 317 Added my implementation
en7 STommydx 2018-01-20 08:09:28 681 Added B editorial (published)
en6 STommydx 2018-01-20 07:13:01 1117 Added D tutorial
en5 STommydx 2018-01-20 04:09:14 2 fix typo
en4 STommydx 2018-01-19 23:15:03 6 Tiny change: 'me? As $x <= 60$, $hh$' -> 'me? As $x \leq 60$, $hh$' (saved to drafts)
en3 STommydx 2018-01-19 23:14:17 95 Tiny change: 'answer is 390 where $x ' -> 'answer is$390$where$x ' (published)
en2 STommydx 2018-01-19 23:04:28 1519 Added A and C tutorial
en1 STommydx 2018-01-19 21:45:30 1539 Initial revision (saved to drafts)