In problems A and G in Codeforces Round #713 (Div. 3) invalid hacks were allowed for some time. Such hacks will be removed, and hacked solutions will be rejudged. ×

(Another) editorial of contest (by egor.anisevich) in honor of ending of academic year and exams

Revision en2, by galen_colin, 2020-06-11 08:49:12

Link to contest

Author's editorial

This is a duplicate of my comment here. I've gotten a couple of requests to make it a blog (for some reason), so here it is. Note: this is unofficial, I was just a competitor


This is mostly about knowing how your language works. Division, however, gets picky. It's a good idea to not use doubles because they have precision issues (read). Instead, you can use integer division, which automatically truncates (in C++, at least), possibly having to be careful with negatives in other languages.

For the actual implementation, it seems like the test cases are designed so overflow won't happen and you won't have to mod with negative numbers, so the rest is simple.

My submission: [submission:83308763]


Consider all numbers that have to be odd. Because numbers are nonnegative, each odd number must be worth at least $$$1$$$. Similarly, each even number must be worth at least $$$0$$$. So the minimum possible number you can create is $$$c_o$$$ — the number of required odd numbers. You can create any $$$s \geq c_o$$$ where $$$s$$$ and $$$c_o$$$ have the same parity (just add $$$2$$$ until you get there), and you can't create any $$$s$$$ of different parity. So the final conditions to check are that $$$s \geq c_o$$$ and $$$s \mod 2 = c_o \mod 2$$$.

My submission: [submission:83309018]


Store any form of set (std::map in C++, java.util.TreeSet in Java, a dictionary in Python, I don't know about other languages) that supports fast insertion and checking if an element is in the set. Then the first type of query is insertion, and the second type of query is checking if an element is in the set, which is perfect.

My submission: [submission:83309210]


This is a direct application of segment trees. I... don't have more to say about this one.

My submission: [submission:83309468]


This is a problem with a simple solution, but one that may be hard to come up with.

First, we must note that this problem is exactly equivalent to finding the number of paths from $$$(1, 1)$$$ to $$$(i, j)$$$ in a rectangular grid, where you can only move down or right (read). Why is this so? It's given in the statement that each value at $$$(i, 1)$$$ and $$$(1, j)$$$ is $$$1$$$. Conveniently, there's always exactly $$$1$$$ path to each of these cells. For all other cells, to get to $$$(i, j)$$$ (in the path problem) there are two places you can come from — either $$$(i — 1, j)$$$ or $$$(i, j — 1)$$$. Thus the number of ways to get to $$$(i, j)$$$ is the sum of the number of ways to get to the two places you can come from.

This gives us a dynamic programming recurrence: $$$dp[i][j] = dp[i — 1][j] + dp[i][j — 1]$$$ where $$$dp[i][j]$$$ is the number of ways to get to $$$(i, j)$$$. Note that this is exactly the same as the one given in the problem.

Now that we've reduced the problem, let's attack the path problem a bit harder. Currently, our solution is $$$O(i \cdot j)$$$, which will certainly not pass. Let's think of the path from $$$(1, 1)$$$ to $$$(i, j)$$$ as a string of the moves we make: D for down and R for right. This string must be of length $$$(i — 1) + (j — 1) = i + j — 2$$$, have exactly $$$i — 1$$$ of D, and exactly $$$j — 1$$$ of R. And our goal is now to count the number of different strings. A bit of combinatorics tells us that there are exactly $$$\dbinom{i + j — 2}{i — 1}$$$ or $$$\dbinom{i + j — 2}{j — 1}$$$ possible strings.

Now we just have to compute the answers under the tight memory conditions. $$$\dbinom{i + j — 2}{i — 1}$$$ is equal to $$$\dfrac{(i + j — 2)!}{(i — 1)!(i + j — 2 — (i — 1))!} = \dfrac{(i + j — 2)!}{(i — 1)!(j — 1)!}$$$. Computing factorials and inverse factorials can be done iteratively with $$$O(1)$$$ space and $$$O(i + j)$$$ time (although $$$O((i + j)\log{(10^9 + 7)})$$$ time is simpler).

My submission: [submission:83310114]


We can maintain the probability of ending in each tunnel iteratively. Let $$$a_i$$$ be the given probabilities for each turn $$$i$$$ from the input, and $$$p_i$$$ be the probability of ending at tunnel $$$i$$$ (for $$$i$$$ from $$$1$$$ to $$$n + 1$$$). Then $$$p_1$$$ is just $$$a_1$$$ because we have exactly that chance of ending in that tunnel. Otherwise, we have a $$$100 — a_1$$$ chance of advancing to tunnel $$$2$$$. Then, at tunnel $$$2$$$ we have a $$$(100 — a_1)a_2$$$ chance of stopping in tunnel $$$2$$$, and a $$$(100 — a_1)(100 — a_2)$$$ chance of advancing to tunnel $$$3$$$. More generally, the probability of stopping at tunnel $$$i$$$ is $$$p_i = (100 — a_1)(100 — a_2) \dots (100 — a_{i — 1})a_i = a_i \cdot \prod\limits_{k = 1}^{i — 1}(100 — a_k)$$$, where $$$a_{n + 1}$$$ is just $$$1$$$ because we have to stop there.

The final answer is then just the expected value, which is $$$\sum\limits_{k = 1}^{n + 1} k \cdot p_k$$$. Be careful with output (use setprecision with a large number of digits in C++) to make sure the jury knows your answer is correct because small-number accuracy is important here.

My submission: [submission:83310636]

Tags whoop, de, doo


  Rev. Lang. By When Δ Comment
en2 English galen_colin 2020-06-11 08:49:12 51
en1 English galen_colin 2020-06-11 08:40:51 5677 Initial revision (published)