Блог пользователя MikeMirzayanov

Автор MikeMirzayanov, 5 лет назад, По-английски

Hey!

As I don't see the post discussing the round, I decided to write this.

I do not participate much in contests now. But I love Code Jam. Thanks to problem writers and organizers!

It seemed to me that this round was no more difficult than the previous one. How do you like it?

I liked the problems:

  • A (tags: interactive, constructive): strange problem — the most naive approach fits into the requirements on the total cost of requests;
  • B (tags: number theory, dp): I wrote some kind of DP with memorization of $$$(a, s)$$$, where $$$a$$$ is the last element in the sequence we already placed (I build in increasing order) and $$$s$$$ is the total sum for the future elements. I don't have a proof why it works fast, probably because of the number of divisors is small.
  • C (tags: combinatorics, recurrence): the last occurrence of 1 is the position of $$$n$$$, so we can divide sequence with this position on two parts and use DP to calculate the answer (don't forget to multiply on binomial coefficient);
  • D (tags: graphs, flows, greedy): I used min-cost-flow to match Ms on the first field and the second in optimal way. I tried all possible values of flow to iterate over all possible pairs of Ms I want to match and took best cost among all choices.
  • Проголосовать: нравится
  • +659
  • Проголосовать: не нравится

»
5 лет назад, скрыть # |
Rev. 2  
Проголосовать: нравится +5 Проголосовать: не нравится

Anyone else wasted a lot of penalty on problem A because I didn't realize $$$n$$$ is constant across all test cases...

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +40 Проголосовать: не нравится

Screencast with some commentary

Is the last problem known? I was just tired of thinking about it and submitted some greedy.

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +31 Проголосовать: не нравится
»
5 лет назад, скрыть # |
 
Проголосовать: нравится +10 Проголосовать: не нравится

You can also directly do D with min-cost circulation. Add a dummy vertex, an edge of cost $$$-L$$$ for some large $$$L$$$ from the dummy to to every M in the initial tiling, and an edge of cost $$$-L$$$ from every M in the target tiling to the dummy.

Then add an edge of cost $$$s (|x_i - x_j| + |y_i - y_j|)$$$ between $$$i$$$th M in initial tiling and $$$j$$$th M in target tiling, and an edge of cost $$$f$$$ from every M in initial tiling to the dummy, and an edge of cost $$$f$$$ from the dummy to every M in the target tiling.

Output cost of min cost circulation plus $$$L \cdot (\text{number of M's})$$$.

»
5 лет назад, скрыть # |
Rev. 2  
Проголосовать: нравится +112 Проголосовать: не нравится

For D, there is a solution with O(r*c) nodes and edges.

It uses min cost max flow. The graph has 2 + 2*r*c nodes. Two nodes are for the source and sink, and there are two nodes for each cell of the grid, representing the two colors. There are 4 types of edges:

  1. Source to initial color, with cap 1, cost 0
  2. Final color to sink, with cap 1, cost 0
  3. Between opposite colors of the same cell, with cap INF, cost 2*f
  4. Between adjacent cells of the same color, with cap INF, cost s

The max flow of this graph is always r*c, and the mincost flow divided by 2 is the answer.

  • »
    »
    5 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +26 Проголосовать: не нравится

    I had a similar solution using flow with demands. The way I thought about it was, let magenta = tile with token on it, green = absence of token. We are given the initial and final positions of the tokens, and we can do the following operations:

    1. create a token anywhere for a cost of $$$f$$$
    2. delete a token anywhere for a cost of $$$f$$$
    3. move a token to an adjacent tile for a cost of $$$s$$$

    Now let $$$S$$$ be the source, $$$T$$$ be the sink, and one token = one unit of flow. Then the operations above correspond to the following edges:

    1. $$$S\to v$$$ with capacity $$$\infty$$$, cost $$$f$$$
    2. $$$v\to T$$$ with capacity $$$\infty$$$, cost $$$f$$$
    3. $$$v\to u$$$ with capacity $$$\infty$$$, cost $$$s$$$, where $$$v$$$ and $$$u$$$ are adjacent tiles

    Then for each initial position of a token, add an edge $$$S\to v$$$ with capacity 1, demand 1, and cost 0, and for each final position of a token, add a similar edge $$$v\to T$$$.

    Min cost of any flow satisfying the demands is the answer.

»
5 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Can you please elaborate solution of Problem C?

  • »
    »
    5 лет назад, скрыть # ^ |
    Rev. 2  
    Проголосовать: нравится +76 Проголосовать: не нравится

    I wrote recursive function long solve(int n, int[] a, int l, int r, int d) meaning:

    • the input array is $$$a$$$ and has length $$$n$$$;
    • function call solves subproblem for subarray $$$l \dots r-1$$$;
    • we need to subtract the value $$$d$$$ from each element of the subarray $$$l \dots r-1$$$.

    The root call solve(n, a, 0, n, 0) solves the problem. In this call it is easy to see that the last 1 in $$$a$$$ corresponds to the position of $$$n$$$. If this position is $$$p$$$ (i.e. $$$a[p]=1$$$, $$$l \le p \lt r$$$ and $$$p$$$ is max), then we have two subproblems $$$[l, p]$$$ and $$$[p + 1, r]$$$.

    We solve the first subproblem: just to recurrence call solve(n, a, l, p, d). We solve the second subproblem: just to recurrence call solve(n, a, p + 1, r, d + 1) (we need +1 for $$$d$$$ because after placing max we have extra pancake under all pancakes of the second subproblem).

    We need to multiply solve(n, a, l, p, d) * solve(n, a, p + 1, r, d + 1). Also, we have many options to choose what elements will work for the first subproblem and what elements will work for the second. The number of ways is $$${r - l - 1\choose p - l}$$$. So multiple the result on it.

    code
»
5 лет назад, скрыть # |
 
Проголосовать: нравится +19 Проголосовать: не нравится

In B the number of edges inside a polygon with $$$a$$$ edges is less than $$$a$$$ so you only need dp values of kind $$$(a, n \% a)$$$.

»
5 лет назад, скрыть # |
Rev. 3  
Проголосовать: нравится +211 Проголосовать: не нравится

I had an $$$O(n \log n)$$$ solution to B. Notice that the answer is of form $$$a + ab + abc + \ldots = n$$$. Rewrite as $$$a(1 + b + bc + \dots) = n \Leftrightarrow b + bc + \ldots = n / a - 1$$$. That is basically the form of the answer for $$$(new~n) = n / a - 1$$$. So to get the answer you can just do $$$ans_n = \max \limits_{d|n} ans_{n/d-1}+1$$$ plus-minus the polygons of size 2 case.

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +25 Проголосовать: не нравится

A bit weird that very trivial A costs more than not-so-trivial D-small.

For me, the round was OK, but, unfortunately, concentrating on D small instead of very doable C-large costed me advancement to the next round. Participating in like 5 contests a year also does not help :)

Anyway, the contest was nice, and it feels good to come back from "retirement" from time to time, so thanks to the organizers!

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +8 Проголосовать: не нравится

What do you think your rating would have been if you actively took part in codeforces contests? (My guess is around 2700)(I'm excited to hear your opinion) Thanks.

  • »
    »
    5 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +315 Проголосовать: не нравится

    I think that if I start writing rounds, then at first the rating after stabilization will be approximately mid-orange. If I write a lot and upsolving, then I suppose that I will become red.

    Unfortunately, every Codeforces round I have to monitor the system, provide technical support, etc. I don't have opportunities to participate regularly. But I love to solve problems.

»
5 лет назад, скрыть # |
Rev. 5  
Проголосовать: нравится +13 Проголосовать: не нравится

The way I did D had (R * C + 3) nodes, 4 * R * C edges: One for each cell, a source, S, and two sink D' and D. We use the two sinks to limit the max Flow. (We want to fix how many Magentas we move to a valid position, and then do flips for the others).

Construct the R * C graph by adding edges to neighbors of inf capacity, SwapCost.

For every cell M that should be G, add edge from S to it, 0 cost, 1 capacity. For every cell G that should be M, add edge from it to D', 0 cost, 1 capacity.

Then fix the number of Ms we want to solve by SWAPing to destination, i, from 0 to M inclusive. Make the edge D' to D be equal to that number. Do min-cost flow. Result for this being fixes is:

flowCost + (total_bad — 2*i) * FlipCost (take min of all of these).

Since you're only increasing the edge once, the flow you computed at the previous step is already valid, so you'll only need to do one iteration to update it.

Locally my code ran in ~0.2 sec for all tests.

»
5 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I had the same idea for B but stuck due to no proof of complexity!

»
5 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I was able to solve only A and B, So this time also no advance to round 3. Also can someone please give out an estimated problem difficulty rating for all the problems ?

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +61 Проголосовать: не нравится

The problems seem too straightforward for a R2.

»
5 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I think C was the most beautiful problem although I got it ac only in last minute :)

»
5 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I was very surprised at problem A. I did B first because I saw interactive and decided to leave it. After solving B I went back to A and did it in less than 10 minutes, and I only took that long because I was sceptical that it could be that easy.

I don't think either of my solutions for B or C were the intended ones — I solved B top down (analysis says solve it bottom up), and I solved C using a segment tree and recursion (analysis says straightforward DP).

D I got only the small and I was a bit fortunate because I had been brushing up on bipartite matchings recently.

In general I found the contest more accessible than most of the round 2s I did in practice. In previous years at my current level I would have expected to progress less than 50% of the time, so today was a good day for me, which I think means the problems were not too hard.

  • »
    »
    5 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    How does your top-down B solution work?

    • »
      »
      »
      5 лет назад, скрыть # ^ |
       
      Проголосовать: нравится 0 Проголосовать: не нравится

      Define a recursion rec(target = X, max_used = M), and begin with rec(N,1) End the recursion as a failure if X % M != 0, or as 0 if X = 0. Then for each factor f_i of X (precomputed for all numbers up to 10^6 using sieve), rec(X,M) = 1 + max_i(rec(X — f_i, f_i)). Obviously if f_i doesn't meet the requirements (i.e. isn't a multiple of max_used), we bottom out and fail.

      Here is my code. My GCJ handle is the same as my CF handle.

»
5 лет назад, скрыть # |
Rev. 2  
Проголосовать: нравится +48 Проголосовать: не нравится

I'm really devastated... my goal for the past 4 years has been to try to get top 1000 for the code jam t-shirt. This year I was finally going to get 950th place by solving A, B, and C. Except when the contest ended, my placement was 1806, not 950. Because my C hidden cases failed.

Because I forgot 1 very trivial, very important line of python at the top of my code.

I will never, ever, forget this fucking line again.

  • »
    »
    5 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +26 Проголосовать: не нравится

    We feel ya :p I remember that I had this goal that I was not able to achieve for a very long time — solve the last problem of a Codeforfes round. Once I was late literally 0.5s cause I managed to upload it, but didn't manage to hit "submit" and it got accepted afterwards. What is more I ragequitted that contest halfway through without reading E and came back to read it out of curiosity some time later (15-30 mins) only to find out that it was algorithmic version of one of my favourite math problems ever. Second time I was solving some other hardest problem of the round, noticed that some variable could overflow int, so I declared it as long long, but computed it as a result of multiplication of two ints while not preceding it with "1ll *", which made it overflow anyway. That was the last straw that made me to finally become fully engulfed by the wisdom of "#define int long long"

  • »
    »
    5 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +3 Проголосовать: не нравится

    Feel for you. Next year! I submitted O(\tilde{O}^2) solution being sure it is \tilde{O}(n) one and then even did nothing last minutes of the contest :'(

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +39 Проголосовать: не нравится

I nailed the third problem 1 second before the end of the contest, though it didn't help me to qualify

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +11 Проголосовать: не нравится

In D, I wrote an algorithm to find random non-optimal greedy answer which I ran for 10^5 times and took the best answer out of them, which passed test 1

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +50 Проголосовать: не нравится

I was a bit salty about cutoff for round 3 being too high. At one point I even considered sitting back and watching ranklist after I solved all visible subtasks. Glad that I didn't do it and solved C large as well.

Anyways these are cutoff for round 3s (or score of 1000th rank in round 2) (if anyone is interested) -
2021 — 66/100.
2020 — 39/100.
2019 — 32/100.
2018 — 44/100.
2017 — 23/100.
2016 — 29/100.
2015 — 22/100.
2014 — 40/100.

  • »
    »
    5 лет назад, скрыть # ^ |
     
    Проголосовать: нравится +31 Проголосовать: не нравится

    I wonder if there is a hard constraint on the inclusion of an interactive problem in each round, but this A is at most a qualification round's problem. B being easier than a lot of A's in the past didn't help, either.

»
5 лет назад, скрыть # |
 
Проголосовать: нравится +12 Проголосовать: не нравится

I was feeling sorry for 1001th rank guy , He literally disqualified for 1 sec

Final time of 1000th rank guy:2:29:13 Final time of 1001th rank guy:2:29:14

»
5 лет назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

I have solved B using a brute force solution with a complexity of around ~36sec.

Code
»
5 лет назад, скрыть # |
 
Проголосовать: нравится -10 Проголосовать: не нравится

for some unknown reasons, in problem B, my code passed both cases:

Code(warning bad formatted)

my idea was just to brute force to start with any number bigger than 2, then either multiple it by 2,3,4,5,6. And check the best result. Such a solution should get wrong answer(according to my knowledge I guess). Can anyone contradict/proof if my solution is correct/incorrect?

»
5 лет назад, скрыть # |
Rev. 4  
Проголосовать: нравится -6 Проголосовать: не нравится

I ranked 1672 in code jam round2 , so sad that I didn't make it to next round. This year contest is much easier. Usually solving two whole problem and an additional small test set will guarantee you with top 1000 and make it to next round, this year even solving three problems cannot pass.