Noobish_Monk's blog

By Noobish_Monk, history, 4 months ago, In English

This blog is a submission for the Third Codeforces Month of Blog Posts, thanks to cadmiumky for the initiative!

Thanks to TeaTime and k1r1t0 for giving feedback on the post.


Hi everyone!

I want to talk about Gale-Ryser Theorem and some of its applications. I've provided proofs for each fact in the blog, they're hidden under the spoilers.


Gale-Ryser Theorem

We have an array of $$$n$$$ non-negative integers $$$a_1, a_2, \ldots, a_n$$$ and an array of $$$m$$$ positive integers $$$b_1, b_2, \ldots, b_m$$$, $$$b_i \le n$$$. The array $$$b$$$ describes a sequence of operations, in the $$$i$$$-th operation we need to decrease the values at $$$b_i$$$ positions by $$$1$$$, formally pick $$$b_i$$$ unique indices $$$j_1, j_2, \ldots, j_{b_i}$$$ and decrease $$$a_{j_p}$$$ by $$$1$$$ for $$$1 \le p \le b_i$$$. We want to know if it's possible to have all $$$a_i \ge 0$$$ after the operations.

Without loss of generality $$$b_1 \ge b_2 \ge \ldots \ge b_m$$$. The Theorem says that it's possible iff $$$\sum \limits_{i=1}^n \min(a_i, k) \ge \sum \limits_{j=1}^k b_j$$$ for $$$\forall 1 \le k \le m$$$.

Necessity
Sufficiency

Proof by induction also suggests a strategy for the construction, which turns out to be the natural greedy strategy — always selecting $$$b_i$$$ maximum positions. However, the proof by induction requires us to do the operations in decreasing order. While it doesn't follow from this proof, it's actually not needed to sort $$$b$$$ to run the greedy. In other words, the order of operations doesn't matter, if we always pick only the maximum positions.

Proof that order of operations doesn't matter

Tasks for practice

Problem from AtCoder ABC

Problem from JOISC 2023

1740F - Conditional Mix


An important special case, all $$$b_i = t$$$. That is, in each of $$$m$$$ operations times $$$t$$$ positions are decreased. Then it is only needed to check that $$$\sum \limits_{i=1}^n \min(t, a_i) \ge mt$$$

Proof

Another example is 1774B - Coloring. Here's the short statement: we have $$$n$$$ cells and $$$m$$$ colors, each cell must be colored. For each color there must be exactly $$$a_i$$$ cells painted with that color ($$$\sum a = n$$$). Also, every window of given size $$$k$$$ cannot have cells of one color.

Solution (yes, editorial for div2B)

Here is a problem where you can try applying the idea yourself 1893D - Colorful Constructive


Dual

There is also a dual version of this problem. Suppose on each operation we decrease at most $$$b_i$$$ elements by $$$1$$$, but the goal now is to get all $$$a_i = 0$$$. Just swap $$$a$$$ and $$$b$$$ and we'll get the same problem as before! Originally all $$$b_j$$$ required $$$b_j$$$ positions in $$$a$$$ and each position $$$a_i$$$ could've been picked at most $$$a_i$$$ times. And here each $$$a_i$$$ must be picked exactly $$$a_i$$$ times (we need to pick exactly $$$a_i$$$ positions in $$$b$$$) and each $$$b_j$$$ can be picked at most $$$b_j$$$ times.

Sometimes all of the operations are the same. Usually in this case we want to find the minimum number of operations $$$m$$$ which we need to make all $$$a_i = 0$$$. Suppose all operations decrease at most $$$t$$$ positions. How do we find the $$$m$$$?

Solution

Right now I can only remember this problem 2181G - Greta's Game, which uses this fact, but this idea appears sometimes, usually where $$$t = 2$$$.

Also an application of this idea in Meta Hacker Cup, problem B


Lastly, there is problem D2 from Open Olympiad 24-25 day 2 XIX Open Olympiad in Informatics - Final Stage, Day 2 (Unrated, Online Mirror, IOI rules), which motivated me to understand this theorem. The fact below is crucial for the solution, so it's under a spoiler as well.

Fact

Full text and comments »

  • Vote: I like it
  • +100
  • Vote: I do not like it

By Noobish_Monk, history, 5 months ago, In English

Hi everyone!

This is a pretty basic blog about dp on trees, nothing advanced. Like always, I write blogs about something which I found both not obvious while solving the problem and it's a general idea which can be used in other problems.

The problem is as follows:

A weighted tree on $$$n$$$ vertices is given, as well as $$$n$$$ values $$$d_v$$$. The task is to run Dijkstra algorithm with these initial values. Usual setup for Dijkstra is $$$d_v = \infty$$$ and $$$d_{root} = 0$$$, but here all vertices have a start value. We need to do that in $$$O(n)$$$.

Solution: Root the tree arbitrarily. All pathes in a tree look like first going up and then going down. We'll do two dfs-es, the first will relax values from children to vertex, the second will relax values from vertex to children. This way every shortest path is accounted.

Code

If we need to run a single Dijksta, $$$O(n \log n)$$$ gets TLE rarely, where $$$O(n)$$$ works. But if we need to run many Djikstras, for example, in binary search or in centroid decomposition, this will give a big speed up.

The problem where this optimisation is used is below, though it's not the hardest part of the problem.

1859F - Teleportation in Byteland

Full text and comments »

  • Vote: I like it
  • +213
  • Vote: I do not like it

By Noobish_Monk, history, 7 months ago, In English

Since we're about to have communication problems on CF rounds, it would be really great if a new tag could be added for them. This way we can separate communication problems from usual interactves, and communication+interactive from the usual communication problems.

Full text and comments »

  • Vote: I like it
  • +213
  • Vote: I do not like it

By Noobish_Monk, history, 7 months ago, In English

Hi everyone!

I need some help with the problem 2147G - Modular Tetration. In the editorial there is this identity

$$$\sum_{x|\varphi(m), rad(x) | k} cnt(x) = \sum_{d_1| q_1^{\beta_1}, \ldots , d_t | q_t^{\beta_t}} \prod \varphi(d_i)$$$

Where $$$k$$$ is a square-free number with $$$gcd(k, m) = 1$$$ and $$$k | \varphi(m)$$$. $$$cnt(x)$$$ is the number of remainders $$$1 \le a \lt m$$$ for which $$$ord_m(a) = x$$$. $$$k = q_1 q_2 \ldots q_t$$$ and $$$\beta_i$$$ is the exponent of $$$q_i$$$ in $$$\varphi(m)$$$

It is some counting in 2 ways, as far as I understand. I tried to prove this for several days, now I am desperate to know the proof since the editorial mentions this as something obvious and the author of the problem doens't respond.

Full text and comments »

  • Vote: I like it
  • +42
  • Vote: I do not like it

By Noobish_Monk, history, 8 months ago, In English

Hi everyone!

This blog is totally not about asking to debug my code

On some recent div 1 rounds there were problems (this 2147F - Exchange Queries and that 2150C - Limited Edition Shop) which had one extra permutation in the input array. There were two given, but one of the permutations could be transformed to identidy. However, in order to do that, I need to permute other arrays accordingly. And that caused me a lot more trouble than I wish it did.

Usually I do something like this

void permute(vector<int> &a, const vector<int> &p) {
    int n = a.size();
    vector<int> tmp(n);
    for (int i = 0; i < n; i++)
        tmp[p[i]] = a[i];
    a = tmp;
}

but then a question arises "Why $$$tmp_{p_i} = a_i$$$ and not $$$tmp_i = a_{p_i}$$$?". And when do I use which variant?

In problem 2150C - Limited Edition Shop I also need to remake $$$b$$$ with a different rule, I can't simply call permute(v, a) and permute(b, a). I can kinda see why it's not working, but I don't see why it shouldn't work. If somebody knows some way to understand how to permute the array depending on the situation, please explain it in the comments.

P.S.: calling permute(p, p) is a bit funny because I give a const and a regular reference at the same time

Full text and comments »

  • Vote: I like it
  • +45
  • Vote: I do not like it

By Noobish_Monk, history, 9 months ago, In English

Hello everyone!

As a person who had two horrible contest, I found a common trend in both rounds. It was due to lack of good samples me believing something that felt obvious but in reality is just wrong. And I wonder, how can I avoid these mistakes in the future.

Some obvious answer, that I usually try do to on the contest, if I struggle, is stress-testing. It's a really useful tool which can really help with either finding small wrong test cases or checking some assumptions about the problem. But sometimes you can't just stress test, because I am not able to bruteforce all matrices or some other object with just absurd number of combinations. So there aren't always dumb solutions which are easy to code and somewhat efficient.

If so, then how often do you usually overcheck the assumptions that you do? I can't write down every step of my solution, as it takes too much time and after a while I'm just left confused on the mess that I've written (even though I use a tablet and technically have infinite board), but I also can't trust in every idea which comes to me (even if it feels obvious, sometimes)

May you share some ideas? Thanks in advance

Full text and comments »

  • Vote: I like it
  • +102
  • Vote: I do not like it

By Noobish_Monk, history, 10 months ago, translation, In English

Hello everyone!

In contests, problems involving range queries appear quite often. Most problems provide queries offline, which allows for greater flexibility in solving them, but often they reduce to using a segment tree or Mo's algorithm. However, sometimes a problem that is easily solvable offline with Mo's algorithm becomes interesting in the online version. In such cases, some people try to maintain something like $$$500$$$ different states of Mo's algorithm — I never really understood what was meant by that.

Today, I managed to come up with an online version of Mo's algorithm that works in $$$O(n^{\frac{5}{3}})$$$ time and memory for usual problems (where moving the pointers is done in $$$O(1)$$$). For asymptotic analysis, I'll assume $$$n = q$$$. I'm not the first to come up with this idea. As it turns out, here и here was a discussion where this idea was proposed, but I also want to do this as I've never heard of it before.

In this blog, I want to break down how to solve a problem from a recent Educational Round using online Mo's algorithm.

2043G - Problem with Queries

This problem has too much going on — update queries and even encoded inputs. Let's first try solving the offline version without update queries. I should clarify that instead of counting the number of indices $$$i \lt j$$$ where $$$a_i \neq a_j$$$, I'll count the opposite — the number of pairs where $$$a_i = a_j$$$. From this value, it's easy to derive the answer to the original problem, but counting pairs with equal values is slightly simpler to maintain.

The problem itself is a simple exercise on Mo's algorithm.

Implementation

I want to look at the asymptotic analysis from a geometric perspective. Let the block size for Mo's algorithm be $$$B \approx \sqrt{n}$$$. Briefly, we divide the plane into squares of size $$$B \times B$$$ and traverse them in the correct order.

Asymptotic Analysis

Now, let's add the constraint of online queries. Unfortunately, in this version, we can't choose the order of traversing the squares, and we might need to enter some square multiple times, so a different approach is needed. Clearly, maintaining just one pair of pointers won't suffice, as answering each query would then take $$$O(n)$$$ time, which is too slow. Let's define a state as all the necessary information we usually maintain for one segment. In this problem that would be frequency array $$$cnt$$$, number of pairs of indices $$$sum$$$ and pointers $$$L$$$ and $$$R$$$. I want to create multiple states so that we can quickly move from one of them to our query.

Let $$$B \approx n^{\frac{2}{3}}$$$. Now, we have few squares — only $$$\frac{n^2}{B^2} = n^{\frac{2}{3}}$$$. I want to create a state in every square, so that I can reach every query quickly. Indeed, within a square the distance between points doesn't exceed $$$2B$$$, which already gives us acceptable query processing time. The algorithm can be described as follows:

  1. Determine the square containing the query.
  2. If no queries have been made in this square before, initialize it in $$$O(n)$$$ time. Create a state at the query point.
  3. Otherwise, we have a state within a distance of at most $$$2B$$$, so we can answer the query in $$$O(B)$$$ time.

Since there are few squares, all initializations will run in $$$O(n^{\frac{5}{3}})$$$ total time, and the total pointer movements will not exceed $$$O(qB) = O(nB) = O(n^{\frac{5}{3}})$$$.

Great, we’ve learned how to perform online Mo's algorithm faster than $$$O(nq)$$$. But can we also support update queries? And if so, do we need to sacrifice anything? The answers to these questions are yes and no! Typically, with update queries, 3D Mo's is used, which introduces time as a third dimension, turning the traversal into a three-dimensional space. To move the third parameter $$$T$$$, we check if the modified position lies within the current state and perform del and add if necessary. But nothing stops us from doing the same for all blocks. Since there are $$$O(n^{\frac{2}{3}})$$$ of them, the runtime remains the same.

With this idea, it’s clear how to solve the problem. And it’s quite easy to implement.

My solution: 329249141

It's truly phenomenal, imho, that such a simple idea as Mo's algorithm can work online, even with updates!

P.S.: Actually, as it turns out, this is more of an interpretation of the problem analysis rather than a different solution. The division into squares — the same as dividing into blocks. Initializing a state in each square is equivalent to precomputing answers for each pair of blocks, and moving the pointers — corresponds to adding elements from the edges of incomplete blocks.

Full text and comments »

  • Vote: I like it
  • +33
  • Vote: I do not like it

By Noobish_Monk, history, 10 months ago, translation, In English

Recently I came across the problem 2046D - For the Emperor!, which reminded me of the existence of L-R flows. I first learned about them a long time ago, but at the time we weren't given any tasks beyond "just implement an L-R flow", so I didn't retain the algorithm and had to relearn it in more detail. Unfortunately, there wasn't a blog on Codeforces that explained everything clearly, so I had to dig through various other resources. I found them here, got a rough idea of the concept, and then had to look up an implementation. I still had some questions about it — it felt a bit like a black box. But at 3 a.m. today, something came to me and I finally understood it all. To save the 5 people who will read my blog from waiting for this insight, I want to explain my understanding of the algorithm here and how to implement it conveniently.

Full text and comments »

  • Vote: I like it
  • +127
  • Vote: I do not like it

By Noobish_Monk, history, 11 months ago, In English

The div 1 today was cancelled, which made me really frustrated, I was really trying to prepare for the round in order to get satisfiable performance, but now I've got to wait a whole other month, and who knows what will happen then. Because of that I'd like to make some suggestions in order to reduce the frustration from lack of div 1 rounds and precedents like today's one.

Idea 1: Currently I see very few people, that aren't GMs, who can AK a div 2 round, so why can't the unrated barrier for div 2 be increased? I think that masters (and maybe IMs too) can participate in a div 2 and it usually doesn't just become speedforces to solve all problems for them.

Idea 2: I think that today's div 1 was cancelled only because the last problem coincided. Which is really a shame since most of the participants wouldn't even get to that problem. I'm not saying that we shouldn't care about LGMs, but maybe a new type of rounds, that has only one of the two problems for div 1 can be made. They would be more often, since usually preparing a 3500 task takes the biggest effort and the most time.

Personally, I like idea 2 more, as it's more of a compromise between idea 1 and current state of rounds. Some of the last problems from div 2 can really be on further positions that they are now, since modern div2F are usually made only for GMs.

All in all, I hope that something will be done, waiting 1 month for a div 1 just to find out that the last problem was on some contest is really frustating.

Full text and comments »

  • Vote: I like it
  • +99
  • Vote: I do not like it

By Noobish_Monk, history, 18 months ago, In English

So recently I became red on CF and my happiness knows no boundaries. For achieving the red color, I would like to thank those who helped me the most.

dmikhalin, my first informatics teacher who inspired me to do competitive programming

gmusya, teacher in B parallel of the main programming club in Russia, who answered many of my questions and helped my a lot

And all the teachers of parallel A!

I'd like to tell a timeline of my progression. Three years ago when I ended 8th grade I got a giant boost of motivation after I learned segment tree and dsu from CF EDU (that's actually useful try it). In summer school I got to expert (that was so cool, I still remember how I solved problem F from div 3, back then it was difficult). Then I continued to grind CF. Before 9th class I learned that there is a strong programming club from Tinkoff, so I decided to try to get there. Easily the best decision, this club gave me a gigantic boost during these three years. I got to parallel B, which I think is the place where you learn most of important techniques.

In 9th class I also managed to get to ROI (Russian Olympiad in Informatics), craziest thing was that I was top 5 among 9-graders from Moscow on regional stage. However I couldn't get silver medal there, by 16 out of 800 points, which was sad. 2 weeks before ROI I also managed to get CM (could've happened a lot faster, but goodbye 2022 killed me).

Summer after 9th class, high motivation to get revenge and again grinding CF. I also really wanted to get Master before school and bit by bit I crawled in the 2100 zone, getting Master on 5th August 2023. After that I participated in two rounds, first one gave me positive delta, I felt worthy of Master, another one — not so good, E was bad :).

Then was a giant pause for rated rounds, I just couldn't find any time. And then I drop back to CM, solving only one problem on div 1... That felt so awful, I needed to comeback asap. Luckily there was div 2 really close, and I managed to solve all 6 problems! I guess a miracle happened or something, but I got again to Master and a really good one, the idea of getting to IM wasn't so distant anymore.

I could tell about experience from other olympiads (IZhO, Open Olympiad and most importantly ROI), but then the post would be too long (and it already kinda is). Shortly, gold in IZhO (very surprising), gold in Open Olympiad (there not so surprised) and ROI... First day had completely destroyed me, instead of getting in top-30 like it was predicted by training tours I got in top-300... I was depressed the whole day after that result, wanted to get the gold medal (and had enough skill), just got really unlucky). Second day was good, I performed like I should've in both days, so I managed to get at least a silver medal, yay...

Growing when you're 2100+ is really slow, div 1's are only twice a month. I thought I would be able to get IM on round 959, when I was in Germany with my parents, but no, and the worst part was that I got totally killed by problem C! Of div 2!!! I think I spent more time on it than on all other problems combined. I felt really sad that day, like something was taken from me. The worst part was that I had only one chance left before SIS (summer camp) and then school. Pinely Round 4 was really strange for me, I solved D in 3 minutes, and problem A-F felt like there was an extra one (probably D). Having 6 problems solved in like an hour, I thought that it's over and no IM, as G is super hard, but decided to give it a try. By some higher powers I managed to solve it 5 minutes before the contest ended (I still don't understand how that happened), but stunning +131 delta and I went to SIS all happy with a new title.

After that getting GM was more of a measure of time, but still an impressive achievement for me. I'm happy that it happened before my 18-th birthday (which is like in two weeks btw!)

So that's my story and my "yay gm" blog (sorry for cringe, I am on cloud nine today).

P. S.: AMA! I'll try to reply fast :)

Full text and comments »

  • Vote: I like it
  • +248
  • Vote: I do not like it

By Noobish_Monk, 22 months ago, translation, In English

Thanks K1o0n for being the mvp of this round.

1992A - Only Pluses

Idea: Vladosiya

Preparation: K1o0n

Tutorial
Solution (Python)

1992B - Angry Monk

Idea: Noobish_Monk

Preparation: K1o0n

Tutorial
Solution (C++)
Solution (Python)

1992C - Gorilla and Permutation

Idea: K1o0n

Preparation: K1o0n

Tutorial
Solution (Python)

1992D - Test of Love

Idea: ArSarapkin

Preparation: K1o0n

Tutorial
Solution (greedy)
Solution (DP)

1992E - Novice's Mistake

Idea: Noobish_Monk, K1o0n

Preparation: K1o0n

Tutorial
Solution (Python)

1992F - Valuable Cards

Idea: Noobish_Monk

Preparation: Noobish_Monk

Tutorial
Solution (C++)

1992G - Ultra-Meow

Idea: Noobish_Monk, K1o0n

Preparation: K1o0n

Tutorial
Solution (C++)

Full text and comments »

  • Vote: I like it
  • +45
  • Vote: I do not like it

By Noobish_Monk, history, 2 years ago, In English

I came up on a problem and got it to solving this one, but now I am stuck. Can someone help please?

How can we efficiently count the number of regular bracket sequences with length $$$2n$$$ that have first closing bracket on position $$$k + 1$$$ (any way faster than $$$O(nk)$$$ or $$$O(n^2))$$$?

Full text and comments »

  • Vote: I like it
  • +3
  • Vote: I do not like it

By Noobish_Monk, history, 3 years ago, In English

If there will be 3 or 4 rounds before rating update on problems, we'll have full empty page. Is it like a challenge that codeforces is trying?

Full text and comments »

  • Vote: I like it
  • +81
  • Vote: I do not like it

By Noobish_Monk, history, 3 years ago, In English

Hello. I've been trying to understand min cost max flow algorithm for several days and now I realised I don't understand why Jonhson's potentials work. We use them so that all edges have nonnegative weights. As I remember, Jonhson's algorithm works only if there aren't any cycles with negative weight. But isn't it the criteria for optimal MCMF, that there are no negative cycles in the residual network? So, until we actually find the optimal flow, we can't use potentials as there are negative cycles. Why can we use potentials then?

Full text and comments »

  • Vote: I like it
  • +16
  • Vote: I do not like it

By Noobish_Monk, history, 3 years ago, In English

Hello. I've heard there is a way to store trees using like 3 integer arrays. How exactly is it done? And can it be extended on any graph?

Full text and comments »

  • Vote: I like it
  • +13
  • Vote: I do not like it

By Noobish_Monk, history, 3 years ago, In English

After rounds problems appear in the problemset and somehow get their difficulties after some time. How is the difficulty selected for a problem and what's the time needed for rating to be determined?

Full text and comments »

  • Vote: I like it
  • +8
  • Vote: I do not like it