Sunday, January 5, 2020

A red maxflow week

Codeforces ran two contests this week. Hello 2020, as the name suggests, was the first round of the year (problems, results, top 5 on the left, my screencast, analysis). Only four contestants could solve the hardest problem G, and only two of them also solved the remaining problems: mnbvmar and TLE. They had roughly the same speed as well, but mnbvmar only had two attempts that failed pretests compared to TLE's ten, and that's what made the difference. Congratulations to both!

I could solve the first five problems reasonably quickly, and I was quite excited about inventing the randomized solution to problem D and quickly recognizing that problem E is more or less equivalent to a very old problem about counting the number of 4-tuples of points that form a convex quadrilateral (I have a feeling that I wrote about it in this blog, but I seem to be unable to find the entry). However, the last two problems proved insurmountable for me, and I spent most of the time trying to get solutions that were clearly not the intended ones to work: max-flow on a graph of size n*log(n) in F (it turns out it was possible to succeed in this way — check out izban's solution as an example), and repeated randomized search in G. I guess the time might have been better spent just thinking on paper, but then the screencast would not be so exciting :)

On a related note, quite a few people have noticed that I've switched to C++ in the recent contests, and asked why. I don't have much to add to this Egor's comment. In the past I have tried switching to C++ a few times and noticed that I keep fighting with it during the contests instead of solving problems, and I do have a similar feeling now as well despite the better tools. However, I will try to keep using C++ for a longer time to see if things improve :)

Here is the hardest problem from this round for you to try as well: you are given a 20x20 grid colored as a chessboard, with the top-left corner colored black. Some of the cells are removed from the grid, the remaining cells form a 4-connected piece and include the top-left corner. You need to insert some walls between the remaining cells in such a way that we get a good labyrinth: there must be exactly one way to get from each cell to each other cell. Moreover, we want each black cell (remember the chessboard coloring) except the top-left cell to not be a dead-end in the labyrinth: each such cell must have at least two accessible neighbors.

Codeforces Round 612 followed a day later (problems, results, top 5 on the left). The sets of problems solved by the top contestants were very diverse (even though not visible in the top 5 screenshot, problem F was also solved by two contestants), but in the end ainta just solved more problems and won. Well done!

In my previous summary, I have mentioned a couple of problems. The first one came from AtCoder: an assignment of integer scores between 1 and n (not necessarily distinct) to n programming contest problems (n<=5000) is called good if for each k the total score of every set of k problems is strictly less than the total score of every set of k+1 problems. How many good assignments of scores exist, modulo the given prime m?

The first step to solve this problem is to notice that we can get rid of "for each k" qualifier, replacing it with just k=(n-1)/2, rounded down. The reason for this is that in case the constraint is violated for smaller k, we can just add the same problems to both sides to reach k=(n-1)/2, and in case it is violated for larger k, the two sets necessarily have an intersection, so we can remove the intersection until we reach k=(n-1)/2 as well. Also, instead of "every set" we can say: the total score of k problems with the largest scores must be less than the total score of k+1 problems with the lowest scores.

To enforce that last constraint, it would be useful if our problem scores were sorted. This can be achieved with the following more or less standard process: we start with all problem scores equal to 1. Now we do the following n-1 times: add 1 to a suffix of problem scores (this suffix could also be empty or all problems).

Now we can keep track how each such operation affects the value we're interested in: the sum of (n-1)/2+1 smallest elements minus the sum of (n-1)/2 largest elements. Going from the empty suffix to the full suffix, they change that value by 0,-1,-2,...,-(n-1)/2,-(n-1)/2 (if n is even),-((n-1)/2-1),...,-2,-1,0,1. Let's denote that multiset of changes as C. In the end we need the value to be positive, and it starts with 1 (when all problem scores are 1).

Our problem can then be restated as follows: consider all ways to choose n-1 values with replacement from the multiset C (the same value can be chosen as many times as we want). How many ways have a non-negative sum?

Since the sum needs to be non-negative and the only possible positive change is 1, this yields a O(n3) dynamic programming solution that can be sped up to O(n2*logn) and get accepted by skipping the states from which we can't reach the final state: let's process the elements of C in decreasing order, and maintain dpi,j,k as the number of ways to choose j values from the first i elements of C such that their sum is k. The answer is the sum of dpn+1,n-1,k over all k>=0.

However, there exists a magical way to speed this up to O(n2). Let's rearrange our dynamic programming slightly: now we will process the elements of C in increasing order, and dpi,j,k will now be the number of ways to choose j values from the first i elements of C such that their sum is -k. Also, we will stop before we process the only positive element 1 (because that would need special handling anyway as the sums stop being only negative), but also (!) before we process one of the two zeros that we have — in other words, we will only consider the first n-1 elements of C in increasing order.

How to compute the answer from the last row of this dynamic programming? Suppose we have computed dpn-1,j,k. Now we need to add some number of 0s and some number of 1s, let's denote those as z and o respectively. We need to have j+z+o=n-1 to get n-1 total changes, and k<=o to get a non-negative sum. The solutions to these two constraints look like: o=k, z=n-1-j-k; o=k+1, z=n-1-j-k-1, and so on until z reaches 0. The number of solutions is thus max(0, n-j-k), so our answer is a sum of max(0, n-j-k)*dpn-1,j,k.

Here comes the magic: since max(0, n-j-k) only depends on j+k and not on j and k separately, and since our transitions just add one to j and the current element of C to k, and so the thing being added does not depend on the values of j and k themselves, we can collapse our dynamic programming states to keep track of j+k only, instead of j and k separately! This means we'll have O(n2) states and O(n2) complexity.

What remains a mystery is: how does one come up with this magic? I guess one could just stumble upon it while trying different approaches. Maybe a more principled way is to use the approach from my old post: if we just implement the O(n3) dynamic programming which processes the elements of C in increasing order, and find out the contribution of each state to the final answer, we can notice that the contributions of states with the same value of j+k are the same and collapse them. Is there any other way that makes this observation look less magical?

The second problem I mentioned was from Codeforces: you are given n integers ai such that i-n<=ai<=i-1 (i goes from 1 to n), in other words one integer from [-(n-1),0], one from [-(n-2),1], ..., one from [0,n-1]. You need to find any nonempty subset with a zero sum. You are guaranteed that such subset always exists, which is by itself quite a hint. n is up to a million.

The key idea here is to realize that keeping integers from different segments is a bit clumsy, so let's shift the segments: the first one by n-1, the second one by n-2, and so on. Now all integers are chosen from the segment [0, n-1] which is nice and symmetric, but instead of a zero-sum subset we need to find a subset where the sum of values equals to the sum of shifts.

To restate, we have reduced our problem to the following: you are given n integers a0, a1, ..., an-1, each between 0 and n-1. You need to find a set S of indices such that Σi∈Si=Σi∈Sai.

When I obtained this reduced problem during the round, it felt really familiar to me, so I've tried to google the answer without much success. It turns out that it's simply quite easy: build a graph from the arrows i->ai, and just find any cycle in this graph. The indices corresponding to this cycle will satisfy the equality above since the sums will just be the same numbers but in different order.

I will be doing a poll for the best problem of 2019 soon, and I will mostly be picking the candidates from the problems I explained in this blog. However, I realize that there were many great problems in 2019 that I just did not encounter, so if there is a problem you feel should be included in the shortlist that was in a contest that I did not participate in, please mention it in comments! Feel free to also post links to similar discussions, such as this post.

Thanks for reading, and see you next week!

Friday, January 3, 2020

A mobile-first week

Last week has wrapped up the competitive 2019 with two rounds. AtCoder Grand Contest 041 took place on Saturday (problems, results, top 5 on the left, analysis). mnbvmar, ecnerwal and Um_nik in first three places all have a different set of problems solved, and mnbvmar's set was the best one. He also tried to submit a solution that just tries random decisions until time runs out for E in the last minute, but that did not fly. Nevertheless, congratulations on the win!

I was writing this round on the go, and around the middle of the round my laptop shut down because of low battery, which was very exciting as you might guess :) After I've turned it back on it continued to work for a long time somehow, suggesting that the problem lies with the battery level detection. A few minutes before the end of the round it shut down again, so I've tried to fix my solution to problem A from my phone. Unfortunately I was a few seconds too slow, otherwise it would have been a nice achievement :)

Problem D in this round had an awesome intended solution, even though most contestants managed to squeeze in a more boring one: an assignment of integer scores between 1 and n (not necessarily distinct) to n programming contest problems (n<=5000) is called good if for each k the total score of every set of k problems is strictly less than the total score of every set of k+1 problems. How many good assignments of scores exist, modulo the given prime m?

This round has concluded the selection of 8 AtCoder World Tour finalists that would come to Japan in February for the onsite finals (results, top 8 on the right). With this win, mnbvmar has jumped onto a departing train (there is such idiom in Russian, вскочить на подножку уходящего поезда, but I'm not sure if there is a direct English equivalent or a different idiom with the same meaning — is there?) and overtook eatmore by just 6 points. See you all in Japan!

Codeforces Good Bye 2019 followed on Sunday (problems, results, top 5 on the left, my screencast, analysis). Not without help from a notorious coincidence and a bad day for tourist, Radewoosh has solved everything, won the round, ended the 2019 top rated, and was still a bit salty. Still, congratulations!

Problem G generated some conflicting opinions, but I have enjoyed solving it quite a bit: you are given n integers ai such that i-n<=ai<=i-1 (i goes from 1 to n), in other words one integer from [-(n-1),0], one from [-(n-2),1], ..., one from [0,n-1]. You need to find any nonempty subset with a zero sum. You are guaranteed that such subset always exists, which is by itself quite a hint. n is up to a million.

Thanks for reading, and see you in the present!

An overall week

Codeforces Global Rounds series of 2019 has concluded during the Dec 16 - Dec 22 week with the Round 6 (problems, results, top 5 on the left, analysis). With problem F being tricky to implement but quite standard, problem G requiring to notice a complex pattern after implementing an ineffective solution, and problem H relying on some cool maths coming almost out of nowhere, there was plenty of ways for top contestants to pick their poison. Only 1919810 and sunset could solve two of those three, and 1919810 got all easier problems correct as well thus securing a confident victory. Well done!

The choice of counting the 4 best performances out of 6 for the overall standings (top 5 on the right) seemed to turn out quite nicely, allowing contestants to skip some rounds and/or to enjoy a really bad day sometimes, although maybe Radewoosh would prefer 2 out of 6 :) Thanks to Codeforces and to XTX Markets for organizing the series!

TopCoder Open 2020 points-based qualification stage 1 also concluded that week, with the SRM 773 (problems, results, top 5 on the left, my screencast, TCO20 qualification standings). There was no stopping tourist this time, with yet another dominating coding phase performance and another 175 challenge points he was simply out of reach and thus finally earned the 5 qualification points he needed to qualify for TCO20. I do not have my TopCoder stats scripts working anymore to support this claim with data, but it feels like he has really stepped up the challenge game a lot recently. Huge congratulations!

Open Cup 2019-20 Grand Prix of Xi'An on Sunday sent two problems to the New Year Prime contest (results, top 5 on the left). Having penalty time almost twice as big as that of the second-placed NNSU Almost Retired, team USA1 had no other way to victory except solving more problems, which they delivered by becoming the only team to solve L in the last hour, and then finally getting F from the 24-th attempt. Congratulations!

With this win, team USA1 has almost caught up with team Past Glory in the overall standings after 2019 (top 5 on the right), promising us an exciting second half of the season in 2020!

In my previous summary, I have mentioned a TopCoder problem: you are given 100000 points on the plane and a number k. Given a subset S of the set of given points, and denoting all other given points as its complement T, we define the score of S as the sum of pairwise Manhattan distances between the points in S minus the sum of pairwise Manhattan distances between the points in T. What is the largest (by the number of elements) subset of the set of given points such that its score does not exceed the given number k?

The key insight in this problem is to consider how does the score of a set change when we add one more point to it. The distances from this point to the points already in S are added to the score; the distances from this point to the points not in S are also added to the score as they used to be subtracted from it and now they are not! Therefore adding a point to S simply increases the score of S by the sum of distances from this point to all other points.

Now it is clear that we should just start with empty S and add points in order by this sum of distances until we exceed k.

Thank for reading, and check back for more!

A generating week

TopCoder returned with its SRM 772 during the Dec 9 - Dec 15 week (problems, results, top 5 on the left, my screencast). Once again tourist had to settle for the second place despite great coding and challenge phases because ksun48 could solve the hardest problem. We were discussing during the TopCoder Open onsite that ecnerwal seems to just think in generating functions, and apparently so does ksun48, even though he tried to hide the fact by explaining his combinatorial-ish solution :) Congratulations on the win!

I was comfortably in the top 10 this time, meaning that both me and tourist each got 4 TCO20 qualification points again and I kept my 1-point lead going into the final SRM of the stage.

The medium problem in this round was very nice. You are given 100000 points on the plane and a number k. Given a subset S of the set of given points, and denoting all other given points as its complement T, we define the score of S as the sum of pairwise Manhattan distances between the points in S minus the sum of pairwise Manhattan distances between the points in T. What is the largest (by the number of elements) subset of the set of given points such that its score does not exceed the given number k?

Codeforces then hosted two rounds on the weekend. Round 606 took place on Saturday (problems, results, top 5 on the left, analysis). With a whole 1 accepted solution for both E and F combined, the round was effectively decided on the first four problems. Ainta was the fastest and won the round, well done!

Round 607 followed on Sunday (problems, results, top 5 on the left, analysis). This round got a quite different top 5, with only Radewoosh appearing in both. ksun48 got most of his speed advantage over Um_nik on the easier three problems, and then kept the lead by solving D and E in a comparable amount of time. Congratulations on the win!

Thanks for reading, and check back for more!

An intriguing week

Codeforces Round 604 took place during the Dec 2 - Dec 8 week (problems, results, top 5 on the left, analysis). MiFaFaOvO has won the round, taking some advantage from the fact that problem E has appeared before, but still solving F in a bit more than half an hour while his competitors could not do that in an hour. Well done!

Open Cup 2019-20 Grand Prix of Beijing wrapped up the week on Sunday (results, top 5 on the left). Team Past Glory has continued to break away in the overall standings, winning the round and solving all problems to boot (they would still have won even without solving D at the end of the contest). Congratulations on the win!

In my previous summary, I have mentioned my NEF problem: there are 2n elements (n>=3). We can compare any two elements, and there are no equal ones, so one will compare bigger than the other. Our goal is to make such set of comparisons that uniquely determines the n biggest elements out of 2n, but not the ordering of those n elements. In other words, there must be at least two possible orderings of those n elements that are consistent with the outcomes of comparisons.

For large values of n, since the n biggest elements can be determined in O(n) and sorting them requires O(n*logn), just applying any O(n) algorithm will do as we will not do enough comparisons to determine the order, and some randomized approaches also work. The real problem lies in tackling small values of n, roughly between 3 and 7.

One could imagine that for such small inputs we could do some kind of exhaustive search, but it turns out that already for n=6 the state space is enormous as we have 12! possible inputs. Therefore, we need to come up with an actual algorithm :)

Initially I did implement the exhaustive search to find a solution for n=3, and then came up with a way to obtain a solution for n+1 from a solution for n. However, together with Pavel Kunyavskiy we could come up with a simpler approach.

Let us take arbitrary n+1 elements and split them arbitrarily into two groups of size at least 2, for example of size 2 and n-1. Now let's find the smallest element in each group in any possible way (using only comparisons within the group), and then compare those two elements between themselves. The one which compares smaller is the smallest among the n+1 chosen elements, and therefore is not among the n biggest elements, so we can discard it from consideration.

Now let's add one more element to one of the two groups in such a way that both have size at least 2, and repeat the step above, discarding one more element from consideration. We repeat this until there are no more elements to add (discarding n elements in total).

In the end we're left with the n biggest elements split into two groups, and we have never compared any element from the first group to any element of the second group, therefore there are at least two (more precisely, at least three) possible orderings of those n elements.

Thanks for reading, and check back for more!

A 4-point week

With TCO19 over, the qualification for TCO20 is well underway, and TopCoder SRM 771 was a part of that during the Nov 25 - Dec 1 week (problems, results, top 5 on the left, my screencast, analysis). Tourist did all he can to bounce back from a resubmit on the 1000-pointer, found 175 challenge points but was just 2 points short to overtake majk with his wonderful 800+-point solution :) Congratulations to both!

I was implementing the solution described in this comment, but an internal assertion kept failing on one of the samples. With just seconds left in the round I've removed the assertion and submitted, but of course it still failed the system tests. It turns out the idea was incorrect as well, but the system tests would not have caught that, so one could say I was close :) Luckily for me, many others have failed the system tests and I barely climbed into the top 10, so both myself and tourist gathered 4 points for the TCO20 qualification standings.

On the ICPC side, the Northern Eurasia Finals took place in St. Petersburg, Barnaul, Tbilisi and Almaty on Sunday (problems, results, top 5 on the left, broadcast, online mirror results, analysis). The strong favorite team NNSU Almost Retired did well but team SPbSU 25 was a bit faster and won, both teams at 10 problems while others got at most 9. Congratulations to both teams!

In the online mirror, three teams placed above them but as I understand none of those are active/eligible for ICPC. Team Cafe Mountain in the 7th place actually is a current ICPC team competing for Seoul NU (please correct me if I'm wrong!)

I have set the interactive problem I for this round, which went like this: there are 2n elements (n>=3). We can compare any two elements, and there are no equal ones, so one will compare bigger than the other. Our goal is to make such set of comparisons that uniquely determines the n biggest elements out of 2n, but not the ordering of those n elements. In other words, there must be at least two possible orderings of those n elements that are consistent with the outcomes of comparisons. Can you come up with an algorithm with a requirement that it does not do something? :)

Note that this would not be possible for n=2, as it might happen that the first two elements we try to compare are the two biggest elements, and thus we would learn a unique order for them.

Thanks for reading, and check back for more!

Thursday, January 2, 2020

A birthday week

Codeforces hosted two rounds during the Nov 18 - Nov 24 week. Round 601 took place on Tuesday (problems, results, top 5 on the left, analysis). Five contestants could solve all problems, and Radewoosh was quite a bit faster than others on problems C and D which gave him the victory. Well done!

Round 602 followed on Sunday (problems, results, top 5 on the left, analysis). This time even more competitors solved everything, and this time it was sunset who was slightly faster than others (some of whom have made great sacrifices to compete). Congratulations on the win!

In my previous summary, I have mentioned a TopCoder problem: you are given three integers n (1<=n<=1011), d (40<=d<=120) and b (5<=b<=62). Your goal is to return any base-b number that has exactly d digits when written without leading zeros, is divisible by n, and has at most four distinct digits when written in base-b without leading zeros.

I have found the correct idea almost immediately after reading the problem: we need to somehow make use of the birthday paradox, which is closely related to meet-in-the-middle algorithm. Roughly speaking, if we generate random numbers, then after generating O(sqrt(n)) numbers we will have generated two that have the same remainder modulo n.

However, it is important to find the right way to apply it. My approach was to split the number in two parts, keep generating random numbers with the same four distinct digits for the first and second part, and wait until we generate two that add up to 0 modulo n. This is not an exact application of the birthday paradox as we have two different random distributions rather than one, therefore it no longer guarantees that we're going to find such pair after generating just O(sqrt(n)) numbers. However, there is a hand-wavy argument: remainders modulo n of such large numbers are more or less random both for the first and for the second part, therefore the birthday paradox should still apply.

Unfortunately, there are cases where the distribution is not random at all. For example, when b=10 and n=1011, all digits before the 11-th one from the end do not affect the remainder modulo n, and therefore all randomly generated first parts will have remainder 0, therefore we'll need on the order of 1011 randomly generated second parts in order to find a match, instead of sqrt(1011). This case is easy to handle separately of course, as we can just include "all zeros" as one of the candidates for the second part. However, there are more tricky cases where gcd(n,b)>1, or when gcd(n,b-1)>1. I have managed to find a way to make this solution pass the system tests, but it was a very dangerous thing to do and the solution could have easily failed.

The right idea is to find a way to apply the birthday paradox directly. Let's generate random numbers of the full length until we find two that give the same remainder modulo n, this is bound to happen within O(sqrt(1011)) steps. The difference between these two is divisible by n, but there are two issues:
  • the difference might have less than d digits
  • even if each of the numbers has only four distinct digits, the difference might have more
It turns out that both issues can be taken care of if we generate random numbers consisting only of digits 0 and 1 in our process. The difference of any two such numbers will have only digits 0, 1, b-2 and b-1, satisfying the at most four distinct digits constraint. And in case the difference has less than d digits, we can just append enough zeros at the end since zero is one of the allowed digits, and appending zeros keeps the number divisible by n.

This solution provably terminates within O(sqrt(1011)) steps with very high probability, and one can submit it with confidence. Note that the fact that we generate numbers consisting only of digits 0 and 1, and therefore their remainders modulo n might not be uniformly distributed, does not hurt us: if the choices are not uniformly distributed, the birthday paradox collision just becomes more likely.

Thanks for reading, and check back for more!