(Note: still massively incomplete and its draft is here just to check CF formatting)
Hello all on Codeforces!
It’s been just over a full year since I created my Codeforces account. Since then a lot has happened on my Codeforces journey so far. With everything that’s happened I want to look back at how I got to my current point, share what I learned in the process, recap some of my best moments, and laugh at whatever I was thinking on a few of these contests, because a lot happened. The only issue is that there’s no way I’m going to detail all 60 contests I took part in 2022. Instead I’ve chosen to “award” the 10 most memorable contests, wheter it be because I did exceedingly well or self destructed in almost hilarious fashion.
A quick note before I begin: No full solutions for any problems are shown in this post but there are code snippets and discussions that are spoilers for these problems. I apologize in advance.
The First Contest — Hello 2022
This was the first contest held in 2022, and the first contest I participated in. At this point my experience with programming consisted of about 4 terms of Computer Science study at SFU, a few offhand miniprojects, a solid math foundation and everything I did on Hackerrank and Codechef. This was the first time though that I felt like I was taking part in a “serious” competition since most of those were longer multi-day contests. It was a pretty rough start for me; I ended up only getting the first problem correct and then I spent the next two hours wondering how I kept getting a TLE. It turned out in Python that using sys.stdin.readline()
was much faster than input()
. Pretty silly mistake, but this was just one of the many things I learned from Codeforces in 2022. Besides, the next few contests would go more smoothly.
The Greatest Blunder — Education Round 123, Problem B In most of my first few contests I mainly did well on math-heavy problems and anything involving the greedy method. My goal in the first few contests was mostly to get used to the two-hour time limit of these contests and avoid making simple errors in logic or implementation. After six contests page explaining why 6 contests are needed to reach “official” rating, my rating normalized to a starting point of 1527. Looking back this was actually a good start, but I was still pretty inexperienced at the time. The next contest “award” explains this better. Prior to this contest, my rating had declined a bit to 1454. After this contest I lost 134 elo, solely from the “I can’t count to five” incident.
I’m not sure if it’s just me, but at this point, I felt like that even though I was technically at the Specialist rating, my knowledge and ability did not resemble that of someone at Specialist. What I mean is that I had some knowledge on concepts that would be found mainly in Expert level problems, but I would also have moments where I make inexplicable mistakes on Newbie or Pupil level problems, such as in this problem, where they reared their very ugly head. This was my second submission in the contest: ``` import sys
""" 1423 4123 1432 4132 """ for i in range(int(sys.stdin.readline())): n = int(sys.stdin.readline()) a = 0 if n == 3: print("2 3 1") print("1 3 2") print("3 1 2") elif n == 4: print("1 4 2 3") print("1 4 3 2") print("4 3 2 1") print("4 1 3 2") elif n == 5: print("5 1 4 2 3") print("5 1 4 3 2") print("5 4 3 2 1") print("5 4 1 3 2")
else: pairs = n//2 for j in range(n): a = j for k in range(pairs): if a % 2 == 1: #reverse order print(n-k,k+1,end=" ") else: print(k+1,n-k,end=" ") a = a//2 if n % 2 == 1: print(n//2+1,end="") print() ``` To summarize, I had cases for n = 3, 4 and 5 hardcoded for this problem, as I had a valid algorithm to generate n Anti-Fibonnachi permutations of length n for cases where n is at least 6. Yet somehow the above solution gets a wrong answer on pretest 2. If you have a keen eye you can probably tell that for the n=5 case, I only wrote out 4 perumations when I needed 5, which was the source of the problem. For those who didn’t, don’t worry.
It took 6 more failed attempts and 84 minutes for me to notice this error.
Needless to say, being able to implement solutions accurately and debug them efficiently is pretty important in competitive programming. I would become more consistent in implementation in later contests, but it took a while for this to be reflected in my overall rating.
Dishonourable Mentions
Round 818, Problem D Actually a double blunder because the first part was forgetting that modular arithmetic has order of operations: ``` Let a = 2, b = 5, c = 3 My line of code: a = a + b % c
What I assumed would happen: a = (2 + 5) % 3 = 7 % 3 = 1
What ended up happening: a = 2 + (5 % 3) = 2 + 2 = 4 ``` As for the second part, it was somehow worse. https://codeforces.net/blog/entry/110917#comment-988527 This comment basically explains it all. Don’t ask how I screwed up basic Python syntax.
Round 823, Problem B, I found a way to pass pretests but fail main tests by not understanding absolute/relative error lol
Pinely Round 1 — I consider a blunder to imply a single catastrophic mistake. As this is more a series of catastrophic mistakes this contest cannot be considered for this award.
The Best Breakout Contest — Round 781
To give a bit of context, it is now the morning of April 8th. I am at a rating of 1398 and am still trying to climb back to Specialist. At this point I am consistently solving Problems A and B with Problem C being inconsistent and Problem D being well out of my current ability. This contest was a case where I solved 3 problems, but more importantly, I actually fully solved Problem D for the first time ever (in a Division 2 contest). It’s understandable that I completely missed Problem C because I was still learning about trees at this time and I was still pretty weak on data structures as a whole. On the other hand, Problem D was more around constructing an algorithm that could determine the value and was much more math and number theory heavy, both topics I was strong at. Even still, how I did on Round 781 was well outside my norm; at this point, this contest was the first time ever that I had a Candidate Master level performance, and was the reason that this contest led to my highest ever rating change of +148.
Honourable Mentions
Educational Round 126 — I didn’t do anything too spectacular in this contest, but I did solve Problems A through C fast enough to reach Expert for the first time. My original goal was to reach Expert in 6 months, but since it was still April at this time I changed this goal for me to be Candidate Master at the end of 2022.
Round 779 — This was technically the first time I solved Problem D in a contest, but it was split into an easy and hard part. I solved the easy part which had around the same number of solves as Problem C had.
Educational Round 127, It was my first time solving Problems A through D in a Division 2 level contest and is tied for my third highest delta gain of +130.
Round 814, I’ll come back to this when talking about Highest Contest Performance.
The Best Single Question — Educational Round 132, Problem D It would take until July 21st to reach the next Codeforces moment. In the past few months I participated in numerous Codeforces contests, and while I was still having contests where I only got Problems A and B, I was getting C with more consistency and occasionally solving D as well. Much of this can be attributed to better data structure knowledge which came from external Leetcode practice (I used the Data Structures I/II study plans for basic review) and reading through https://cp-algorithms.com (very useful resource containing just about everything useful for competitive programming). The only way I can really explain this moment is by my method to solving this problem, so alas, spoiler tag is absolutely needed here.
Spoilers for 1709D: With this question, the first thing to do for each query was to check if the horizontal and vertical distances to move are both divisible by k. If this is the case then the robot can try moving as far up as possible, then move to the correct column, and then move down to the goal. The only thing to look for is if any columns the robot passes through horizontally are blocked. This can simply be done by taking the maximum value of the segment of the array describing these columns.
The issue here is that one cannot simply just call max(A[i:j])
to get the maximum value of a segment of an array each time. In the worst case each query call would take O(m) time to run, making the entire program O(mq) which is too long. Both m and q have limits of 200000, so this means each query should take at most O(log m) time. This is where I conveniently learnt about the segment tree a few weeks prior, which can find the minimum of any segment of an array in O(log n) time. A sparse table can be used to accomplish this task in O(1) time but I didn’t know about it at the time, and with some optimization I successfully solved this problem.
For this problem I actually ended up submitting with an Accepted verdict 3 times. This was due to me doing wack stuff to try and optimize my solution since my method was just fast enough to pass all the testcases, clocking in at 1918ms for a 2 second time limit. Even more fun is that there were 16 attempts made to hack this solution which all failed. I don’t care that I missed Problem C in this contest, for me, this question alone made this the most glorious loss of 3 rating points in a contest ever.
Honourable Mention Global Round 21, Problem E A combinatorics problem with some tricky math shenanigans that I figured out, and is imo the hardest 2000-rated problem I solved this year.
Best Overall Performance — Round 814 This contest can be described as a combination of my effort from the start of 2022 and a bit of luck in the problem topics used. At this point I was usually getting 3 or 4 problems in each contest, and my average performance was at an Expert level. At this point I was still at an 1834 rating.
This contest I technically solved 6 problems. It’s really 5 since Problem D was split into two parts but it’s 2023 and I am still amazed I pulled this off. It wasn’t a perfect contest since I did have a few wrong submissions on D, but an important difference here is that I actually used the feedback correctly to fix my code. In past contests when I get something like Wrong answer on pretest 2
I would be fixing my solution in the wrong method by trying to make small adjustments to an algorithm I believed to be “almost” correct or by trying to account for edge cases. In this case I ended up pretty much creating a new solution after each attempt which led me to the right answer effectively. Then there’s Problem E. To be honest, I still have no idea how I solved this in 15 minutes. My thought process for E is in the spoiler below:
I had about 21 minutes in the contest remaining to try to figure out E, so I wasn’t going to try anything too complex. My first thought was to try making a scuffed greedy solution; first I check if the number of letters given can create a Fibonnachi string which is determined by checking if the number of letters equals the sum of the first x Fibonnachi numbers, x being an integer. If yes, then I just assign each Fibonnachi block of letters to the largest allowed value in the array given, going from largest to smallest. For instance, suppose I had 33 letters: 14 a’s, 7 b’s, and 12 c’s. A Fibonnachi string is possible because 1+1+2+3+5+8+13 = 33. a is the most frequent letter so start with 13 a’s at the end of the string. This leaves us with 1 a remaining. Now c has the most remaining letters, so 8 c’s are used for the next block. Then 5 b’s are used, then 3 c’s, 2 b’s, 1 a, and 1 c to get the Fibonnachi string “cabbcccbbbbbccccccccaaaaaaaaaaaaa”. My reason for this working was that Fibonachi numbers were involved and that there are a bunch of patterns involved with adding each other, so probably a greedy solution would work. And it did.
Honourable Mention (CodeTON Round 2)[https://codeforces.net/contest/1704], approximate contest performance: 2170. I completed Problems A through D in 40 minutes with no wrong submissions. Problem D in particular was a 1900 rated problem that I crushed.
After Round 814, I reached my current peak rating of 1981 and had successfully reached Candidate Master in 2022. Sure this was partially due to the contests having a focus on topics I was strong at and there was a good chance I would regress to my actual skill level eventually, but the progress up to this point was something I was proud of. I felt that I made great improvement in my algorithmic knowledge, and had learnt many useful data structures. I found myself to have improved in competitive programming as a whole, and was hopeful as I want to go as high as possible, not just Candidate Master, but Master and beyond. This graph showing my progress from my 6th contest where my displayed rating matched my actual up to this point was a culmination of all the hard work I put in, and I was proud of it.
[insert image here]
There’s just one issue though: Round 814 occurred on August 16th. If 2022 ended here, then this wouldn’t be an interesting journey now, would it?
Most Painful Choke — (Round 818)[https://codeforces.net/contest/1717] I lost Candidate Master right after achieving it in the very next contest I participated in. Such is what happens when the previous contest plays into all of your strengths. Losing Candidate Master isn’t what I consider my most painful choke though, as Round 818, the contest right after that was much worse. At this point I’m at a rating of 1883 so I’m in striking distance of returning, but unfortunately I performed terribly. Statistically it doesn’t look that bad, I only lost 3 rating from this contest, but I genuinely still have nightmares from my ineptitude in this contest. For example, Problem C. It doesn’t look like I screwed up here at first because I only used 1 attempt and 38 minutes to solve this question but this should’ve been much quicker because I initially tried to solve more than the problem was asking. Consider the following problem:
Suppose I gave you only pencil and paper, and I asked you if 420.69 multiplied by 3.141592653589793238 is greater than 1300. How would you solve this problem?
What you would most likely do is approximate the value by rounding the values. For instance, 420 times 3 is 1260. 10% of 420 is 42, so 420*3.1 = 1260 + 42 = 1302, which is greater than 1300, thus 420.69*3.14159… > 1300.
Notice that for the above problem, you did NOT have to calculate that 420.69*3.141592653589793238 is equal to 1321.63661343869011729422. The question only asks if the multiplication is above 1300, not for its exact value which is unnecessary information. Solve only what the problem requires you to solve.
This mistake is nothing compared to Problem D though. In fairness the question was a complex combinatorics problem that I eventually solved with 4 wrong attempts. That’s where the positives end. Submission #1’s error can be summed up as follows: ``` Let a = 2, b = 5, c = 3 My line of code: a = a + b % c
What I assumed would happen: a = (2 + 5) % 3 = 7 % 3 = 1
What ended up happening: a = 2 + (5 % 3) = 2 + 2 = 4 ```
I didn’t notice the modulo order of operations mistake in the second submission, instead thinking that part of my code was buggy. This part in question was literally from a library of competitive programming code that I had created and tested a month before this contest. The good thing is that I found the modulo error in my third submission. The bad part is that I accidentally screwed with my library code. 3rd submission error, I have no words for this one. The 4th failed submission? I quote from my personal journal entry, “I thought print(ans%m)
wasn’t working properly.”. ans
and m
are int
variables, and unsurprisingly, that wasn’t the problem.
This is a case where I was panic submitting. I can only assume I kept rushing because the point value of Problem D was dropping by 8 points per minute, but this entire debacle cost me 200 points in wrong submissions, not to mention additional points lost from trying to fix these wrong submissions. Had I properly fixed the modulo error after the first submission and not tried a desperate hack attempt at the end of the contest, instead of losing 3 rating, I would have gained around 20 rating and would have made it back to Candidate Master. I completely blew this chance due to prioritizing speed over accuracy. I was still close to Candidate Master, but just the idea of “What if I wasn’t being an idiot in those 4 wrong submissions?” made this an agonizing contest to start September.
Round 818 is also painful for one more reason: this is the start of the Implosion. Dishonourable Mention
(Educational Round 123)[] is obviously here for “I can’t count to five” reasons, but I consider this contest to be more of an implosion.
Round 825, Problem B, 7 wrong submissions, many of which were avoidable, cost me many positions on the leaderboard because Speedforces is a thing.
Before the next award, let’s briefly look at the 10 contests I participated in after Round 818 and how I did on them:
Round 819: Topics I’m weak at, contest ended up being unrated Edu Round 135: Python dictionaries punch me in the gut. This contest also unofficially wins the Most Heartbreaking Contest award as if Problem C was right, I would have again made it to Candidate Master. The one bright spot was that since my solution got hacked, instead of having false hope for 12 hours I only had it for 30 minutes. Round 821: The order in which I read the questions was A, B, C, D1, D2. I ended up solving them in the order B, D1, A, C. I struggled on a Division 2 Problem A. Round 822: Contest at 5am local time that I did well on, even if I only gained 9 rating. Round 823: System failed B because my brain forgot how absolute and relative error worked. I also inexplicably thought to go for Problem E with a total of 5 people solving it when Problem D had nearly 300 solves so add poor contest strategy to the mix. Edu Round 136: Questionable contest strategy (again with attempting E before D because “oh no! trees!”), but aside from that I did okay (solved A-C) and somehow gained 29 rating. Global Round 22: The only other contest besides Round 822 that I feel I did well on, gained 71 rating. Round 824: Problem D is solved, but slowly and with sloppy execution. Round 825: 7 wrong submissions on problem B and I get SpeedForced, ie. lose rating mainly due to solving the same problems as a lot of other competitors slower and with less accuracy. Round 833: Went into this contest after taking a month long break from CodeForces because after Round 825, my mentality was a wreck. Spent the month long break still competitive programming but doing absolutely nothing involving CodeForces, did Round 829 as a virtual contest to get myself back into rhythm which went decently well, then proceeded to over complicate Problems B and C and miss Problem D to lose even more rating.
I was at a rating of 1981 on August 16th. It took under 3 months for me to drop to 1673, 308 points below my peak. Before this slump the furthest I was below my peak was 207 rating points, and that was after not counting to five properly. Despite this, the most important part was that I was learning from these contests, even if my rating was collapsing. Besides, I have basically hit rock bottom at this point, so any decent contest will help in bringing my rating back up. Speaking of, what’s the next contest award I’m discussing?
Most Spectacular Implosion — Pinely Round 1 Ah. This contest. I think I’ll just let this personal journal entry explain how truly “Special” my performance was on this contest.
The above image is clickable by the way.
This week, on Days of Our Programmers,
Our protagonist's CodeForces journey has become one of so much drama comedy that the production crew has decided to make this the first relatively public episode. From where we last left off the Proponent for Python had returned from a one month hiatus from CodeForces. He took that time to mentally reset and prepare himself through logical preparation methods such as doing competitive programming stuff that isn't just CodeForces, actually getting enough sleep, and trying to convince himself that he's a dragon. Truly a man of logical reason. Currently 227 ELO away from the glory of having a purple username the next contest is of critical importance for our protagonist. Another choked competition here may turn his hope of escaping Expert rating hell into nothing. Thus at 6:30am our protagonist readies himself for Pinely Round 1.
As the contest begins the dragon strikes with fury solving Problem A in 4 minutes. Not a bad start but then Problem B comes in and suddenly our protagonist finds himself mindblocked. After half an hour of greedy attempts, the Proponent decides to move to C for now. It starts off well; the protagonist determines that the hierarchy of sets can be represented as a graph structure. For instance, if A subsets B and B subsets C, then node C is the parent of node B which is the parent of node A. Wonderful. The Proponent quickly begins work on representing the set structure as a tree. It is at this point where an old bogeyman rears its ugly head:
Runtime error on pretest 2
Runtime error? Not wrong answer? Clearly just a simple mistype in the code. And there was an error. But the runtime error remained. Our protagonist went through the code rationally yet no mistake was found. Desperation began to increase. Then, a brilliant move was made: he changed the Python compiler from PyPy 3-64 to PyPy 3. Marvel at the Proponent's genius as he gets another runtime error? Huh. I swore that would work. If you couldn’t tell that was blatant sarcasm, and at this point, I have to ask, what in the hell am I doing? I’m blowing yet another contest and it’s my own damn fault because I am turning to inane measures that clearly don’t work. Is this not what the break was for??-
PLEASE STAND BY…
The search continues as he finally finds the problem as 40 minutes and [REDACTED] number of attempts. Turns out the set structure was not a tree because a set could have multiple direct parents. In this case, suppose A = [1,2], B = [1,3], C = [1]
. This results in C having A and B as direct parent nodes, which is not possible in a normal tree. There is no time to lament this classic case of alxwen711 imploding in yet another contest though, for time is running short. Our protagonist rushes to rectify his set graph and with 20 minutes to go, he submits and passes pretest 2. It may not be victory, but at least Problem C is solved.
That is until the program runs out of time on pretest 5. Time is running short, and a quick look at his scuffed code shows that the slowdown was in scuffed implementation. Knowing his solution is logically correct, desperation creeps in again. Many optimizations occur, yet it falls short. And then, time runs out. For the first time since April, our protagonist only solves 1 problem. The collapse has been completed. But in this collapse, a goal is achieved: The Proponent of Python is no longer in the ELO hell that is Expert. A truly amazing result. There is absolutely no copium involved with our writer of this episode here. As our protagonist finds that his contest of self destruction has landed himself in truly a "special" place, what shall he do to cope with this new predicament? Perhaps he'll be more inclined to take part in the dark arts of C++ to distract himself that pretty much all of these failures had nothing to do with language choice and more to do with overthinking solutions and abysmal implementation skills. Whatever the Proponent of Python decides to do in response to this dumpster fire, we can only hope that he recovers his mental for the next episode, of Days of Our Programmers.
448 points below my peak. In 96 days, I went from breaking into Candidate Master to dropping out of Expert entirely. There’s no point in breaking down what went wrong here, because the only thing I did right was solve A quickly. I had a bigger question to ask myself, being “What am I doing?”.
I’ve seen many posts on this site from users asking how to reach x rating. Part of my reason for writing this post in the first place was to show how I reached each rank up to Candidate Master. But there’s only a few posts asking how to deal with a rating slump, and to say I was in one of those is an understatement. It felt horrible and left me questioning if I was doing anything right if rating wise I was at the same point as I was in February. I questioned if reaching Candidate Master the first time was only due to luck. I was coping with this failure through catharic mockery of my implosions.
Never at any point though did I consider quitting. Even with this massive rating loss, at the end of the day, it’s just a number. I may care about it a lot, but it didn’t change the fact that I was more knowledgeable from these contests. If that 1981 peak was an overestimation of my true skill, what’s stopping me from saying that my current 1533 rating is an underestimation? And at this point, I have dropped so far that any further losses would matter little to how numb I was to them. There is nothing left for me to lose if I go into another contest, and I would not forgive myself if I simply stopped here. In my mind, I had to keep going. Pinely Round 1 did not crush my spirit to compete, but ignited it to an admittedly infernal rage that drove me with the sole purpose of redemption.
On a lighter note, it’s a good thing I don’t use this exact same mentality toward gambling.
Comeback Contest of the Year — Round 836 Round 836, for being much needed after the Pinely disasterclass.
Honourable Mention Global Round 22, similar situation as above but to a lesser extent since I at least had some decent contests beforehand.
The Greatest Contest — Polynomial Round 2022 For the contests that were most memorable not by good or bad performance, but by the ensuing chaos during and after the contest. Usually featuring main test fails.
Honourable Mention Education Round 132, the segment tree shenanigans also came with many attempts to hack my solution with TLE attempts. They didn’t work.
Educational Round 136 and Global Round 22, both contests on their own weren’t too chaotic but were back-to-back and both featured problem B having a large number of main test fails. Funny coincidence.