### 1-gon's blog

By 1-gon, history, 2 months ago,

## What's a Voronoi Diagram?

Given a set $S$ of $n$ points in the 2D plane, the Voronoi diagram is a partition of the plane into regions. The region associated with a point $p\in S$ contains precisely the points $q$ such that $q$ is closer to $p$ than any other point in $S$. In other words, a point $q$ belongs to the region of its nearest neighbor.

The Voronoi diagram can be represented by the planar graph of boundaries between regions. A vertex in this graph is where three or more segments meet (or a point at infinity), and an edge is a segment connecting two of these vertices. The way the regions were defined, we see that a segment contains the points equidistant to two points in $S$, so it is part of their perpendicular bisector. Similarly, a Voronoi vertex is equidistant to three or more vertices in $S$, so it is their circumcenter. Recall that the circumcenter is the intersection of perpendicular bisectors.

## What's a Delaunay Triangulation?

The Delaunay triangulation is the dual graph of the Voronoi diagram. That is, a Voronoi vertex is a Delaunay face and a Delaunay vertex is a Voronoi region. A Delaunay edge connects two points if and only if their corresponding Voronoi regions share a border.

In the following image, the set $S$ corresponds to the black points. The red vertices and edges are the Voronoi diagram. The black points and edges are the Delaunay triangulation.

The Delaunay triangulation only involves edges between existing points, while the Voronoi diagram creates new vertices and needs a way to represent edges going infinitely in one direction. For this reason, it is often more convenient to compute the Delaunay triangulation and only convert to the Voronoi diagram if needed.

## Why are these useful?

The definition of the Voronoi diagram immediately shows signs of applications. Given a set $S$ of $n$ points and $m$ query points $p_1,\ldots, p_m$, we can answer for each query point, its nearest neighbor in $S$. This can be done in $O((n + q)\log(n+q))$ offline by sweeping the Voronoi diagram and query points. Or it can be done online with persistent data structures.

Other than this, the Delaunay triangulation has many amazing properties of its own that can make it extremely useful for solving problems.

• For each Delaunay triangle, its circumcircle does not strictly contain any points in $S$. (In fact, you can also consider this the defining property of Delaunay triangulation)
• The number of Delaunay edges is at most $3n-6$, so there is hope for an efficient construction.
• Each point $p\in S$ is adjacent to its nearest neighbor with a Delaunay edge.
• The Delaunay triangulation maximizes the minimum angle in the triangles among all possible triangulations.
• The Euclidean minimum spanning tree is a subset of Delaunay edges.

## Degenerate Cases

Because this is geometry, there are some degenerate cases I have hidden from you up to this point. The first case is when all the points in $S$ are contained in a line. In this case, the Voronoi diagram has all edges connecting two points at infinity, and the Voronoi diagram is disconnected (in all other cases it's connected). Also in this case, the dual graph is no longer a triangulation. We may still decide to represent it by the path of edges connecting the points.

Another nasty case is co-circular points. Here, there are Voronoi vertices where more than three segments intersect. In this case, the dual graph has a face with more than three sides. However, we want it to be a triangulation, so we can also say that any triangulation of such faces is valid, and we will allow non-unique Delaunay triangulations.

## Algorithms

Many algorithms exist to compute the Delaunay triangulation and/or Voronoi diagram of a set of points. Each have their advantages and disadvantages.

#### Flip Algorithms

In this approach, you start with an arbitrary triangulation of the points. Then you repeatedly fix adjacent triangle pairs until the triangulation has the Delaunay property. The main advantage of this method is its simplicity: an arbitrary triangulation can be constructed with an incremental convex hull algorithm, and the disadvantage is that it could require $\Omega(n^2)$ flips in the worst case.

#### Incremental Algorithms

In this approach, you always maintain the Delaunay triangulation of a subset of the points and insert the points one at a time. When a point is inserted, you should delete all triangles whose circumcircle contains the newly added point. After deleting this connected set of triangles, you should repair the face it exposes. For each edge in this exposed face, you should add a triangle with that edge and the new point. This gives the straightforward $O(n^2)$ Bowyer-Watson algorithm.

If we insert the points in a random order, we can significantly reduce the number of triangle insertions and deletions. But to get expected $O(n\log n)$ runtime, we would need to avoid processing all the unaffected triangles. And this makes the implementation far more challenging. We need to maintain a search structure of the triangles in order to locate which one contains the new point.

The advantage of this method is that it achieves expected $O(n\log n)$ runtime with only integer computations, and the construction is online. The disadvantage is that it has a bad constant, isn't deterministic, and the triangle search structure can make for a tricky implementation.

#### Divide and Conquer

The divide and conquer algorithm first sorts the points by $x$-coordinate. Then it splits the points into two sets, recursively computes their Delaunay triangulations, and merges them together. As long as you can perform the merge in $O(n)$ time, the overall algorithm is $O(n\log n)$. When merging the triangulations, it is actually quite hard to keep track of the important adjacency information. The most popular strategy is perhaps the Quad-Edge data structure.

The advantage is that this gives a deterministic $O(n\log n)$ algorithm with only integer computations. The disadvantage is the complexity of the quad-edge data structure and the costly memory usage that comes with it.

#### 3D Convex Hull Reduction

An interesting fact is that the problem of Delaunay triangulation can be reduced to 3D convex hull. If for each point $(x,y)$ we also give it the $z$-coordinate $x^2+y^2$ and compute the 3D convex hull, then the downward-facing convex hull faces correspond exactly to the Delaunay triangles. The advantage is that if you happen to have a good 3D convex hull implementation, you get Delaunay triangulation for free. The disadvantages are that $O(n\log n)$ 3D convex hull is a whole beast of its own, and the large $z$-coordinates will exacerbate precision and overflow errors in the 3D convex hull implementation. The main strategies for 3D convex hull are incremental and divide-and-conquer algorithms, which are only more complicated than their counterparts in the special case of Delaunay triangulation.

My previous blog on 3D convex hull gave an $O(n\log n)$ randomized incremental implementation, but it turns out to have quite a bad constant for practical use.

## Fortune's Algorithm

In this blog, I will focus on Fortune's Algorithm. It takes a sweep-line approach to the problem, and achieves a deterministic $O(n\log n)$ time complexity. The main disadvantage is that computations are done with floating points instead of integers, opening up the door to precision errors. However, keep in mind that the algorithms using only integers have a risk of integer overflow even on reasonably small constraints.

The main challenge in designing a sweep-line algorithm for Voronoi diagrams is that a point after the sweep-line can affect the diagram before the sweep-line. That is, if the sweep-line denotes the discovery time of a point, we cannot know the entire Voronoi diagram before the sweep-line. The way Fortune's algorithm overcomes this challenge is by maintaining the Voronoi diagram only for the area we can be certain about, regardless of what points lie after the sweep-line.

Now, which points can be affected by what lies past the sweep-line? Well, such a point must be closer to the sweep-line than to any point in $S$ to the left of the sweep-line. Recall that the set of points equidistant to a point and a line is given by a parabola, where the point is called the focus and the line is called the directrix. If the focus is a point $p\in S$ to the left of the sweep-line and the directrix is the sweep-line, then everything to the left of this parabola we can be certain of its region. Now, if we make such a parabola for every point $p\in S$ to the left of the sweep-line, everything to the right of all parabolas is precisely the uncertain region. The boundary of the certainty and uncertainty regions is called the beach line, consisting of parabolic arcs.

We are ready to understand Fortune's algorithm abstractly. We scan a sweep-line from left to right, maintaining the combinatorial structure of the beach line. The parabolas are only for visual aid, and the ordered list of foci is what is actually stored. As points get added and parabolic arcs shrink to length $0$, these events tell us precisely the structure of the Voronoi diagram and Delaunay triangulation.

There are two types of events which change the beach line structure: point events and vertex events.

#### Point Events

In a point event, the sweep-line discovers a point $p\in S$ and inserts its parabolic arc into the beach line. In order to do this efficiently, we need to search for where it belongs in the beach line. Then it will split the arc it sees into two. An example of a point event is shown below (here the sweep-line goes from top to bottom instead).

#### Vertex Events

In a vertex event, a parabolic arc disappears from the beach line, corresponding to a Voronoi vertex. To handle a vertex event, we simply remove the point whose arc length shrinks to length $0$. Such an event is generated dynamically by a triple of points adjacent on the beach line. Every time we modify the beach line, we update the relevant points for their vertex events. To compute the time a vertex event should be handled, we take the circumcircle of the three points generating the event. The rightmost point of the circumcircle is the time we should process this event.

#### Summary

In summary, the algorithm maintains a priority queue of events. Initially, we push all point events into the priority queue. When processing a point event, we find the correct arc in the beach line, split it up, add the new point, and add new vertex events if needed. When processing a vertex event, we add the generating triple as a Delaunay triangle, remove the arc that should disappear, and add new vertex events if needed.

## Fortune's Algorithm Implementation

My code doesn't work when two points have the same $x$ coordinate. This is handled simply by rotating all input points by $1$ radian and praying to the geometry gods.

Implementation

## Example Problems

Panda Preserve solution
Caravans solution
Good Manners solution

## Resources

• +499

By 1-gon, history, 3 months ago,

I hope you enjoyed the problems! Implementations will be added soon.

1450A - Avoid Trygub

Tutorial

Implementation

1450B - Balls of Steel

Tutorial

Implementation

1450C1 - Errich-Tac-Toe (Easy Version)

Tutorial

Implementation

1450C2 - Errich-Tac-Toe (Hard Version)

Tutorial

Implementation

1450D - Rating Compression

Tutorial

Implementation

1450E - Capitalism

Tutorial

Implementation

1450F - The Struggling Contestant

Tutorial

Implementation

1450G - Communism

Tutorial

Implementation

Tutorial

Implementation

Tutorial

Implementation

• +382

By 1-gon, history, 3 months ago,

¡Buenos días! (That's Spanish for "what's up homies")

On Dec/06/2020 17:35 (Moscow time) we will host Codeforces Global Round 12.

It is the sixth round of a 2020 series of Codeforces Global Rounds. The rounds are open and rated for everybody.

The prizes for this round:

• 30 best participants get a t-shirt.
• 20 t-shirts are randomly distributed among those with ranks between 31 and 500, inclusive.

The prizes for the 6-round series in 2020:

• In each round top-100 participants get points according to the table.
• The final result for each participant is equal to the sum of points he gets in the four rounds he placed the highest.
• The best 20 participants over all series get sweatshirts and place certificates.

Thanks to XTX, which in 2020 supported the global rounds initiative!

The problems were written and prepared by smart Cuban Devil and stupid Americans fivefourthreeone and 1-gon.

We would like to distribute our thanks equally to the following people who made this round possible.

You will have 3 hours to solve 8 problems (and 2 subtasks). If you want to lose rating, then we encourage you not to read all the problems.

May rating be distributed from each according to his ability, to each according to his needs!

UPD: Here's the score distribution. Good luck, have fun!

$500-750-(1000+750)-1750-2500-2750-3750-(2750+1750)$

UPD: Hope you enjoyed the problems! Editorial is posted.

UPD: System testing finished, congrats to the winners!

Announcement of Codeforces Global Round 12

• +1239

By 1-gon, history, 3 months ago,

The upcoming round has an unusual starting time. In order to keep my performance slightly less bad than usual, I will apply the "destroy my sleep schedule" strategy. For some unknown reason I have decided to document this by beginning my streaming career.

https://www.twitch.tv/monogoncoding

• +266

By 1-gon, history, 4 months ago,

Randomization is known to be a powerful algorithmic technique, and I want to start a discussion about it specifically in the context of competitive programming. What extra considerations should we make about how online judges work when analyzing our randomized algorithms, and approving such problems for an official contest? In particular, I want to discuss algorithms whose running times are uncertain.

Let's say that the running time of an algorithm is a random variable $X$. Typically, we might say the algorithm is acceptable if the expected running time is a fraction of the time limit: $E(X)=T/k$. With Markov's inequality, we can say $P(X>T)\le 1/k$. That is, the probability of failing a test case due to timeout is at most $1/k$.

## TLE Trick

Online judges usually run your program a few times before giving the Time Limit Exceeded verdict. This fact is known to help randomized algorithms, as mentioned here: https://codeforces.com/blog/entry/78817

Let's apply this in the context of random running times. If your program is run $n$ times, the actual probability of failing a test case is at most $1/k^n$. If there are $m$ test cases total, the probability of failing at least one of the test cases is $1-(1-1/k^n)^m$

## Solving for $k$

Let's suppose we call a program acceptable if its probability of failure is at most $p$. Then we are interested in what the constant $k$ should be in order to be confident of success.

$k\ge \left[ 1-(1-p)^{1/m}\right]^{-1/n}$

Let's plug in some values. Suppose there are $m=200$ test cases, your program gets $n=3$ chances per test case, and we can tolerate a failure probability of $p=10^{-3}$. Then we should require $k\approx 58.47$, which is quite a considerable constant factor that we should be careful about when making randomized solutions.

## Future Directions

If we make extra assumptions on the distribution of $X$ that apply widely to randomized algorithms, can we be confident with a much smaller value of $k$? What will the analysis look like on some specific cases like treaps? I'm interested to hear your thoughts.

• +14

By 1-gon, history, 6 months ago,

I hope everyone enjoyed the contest!

UPD: Added implementations for all problems.

1405A - Permutation Forgery

Author: antontrygubO_o

Tutorial

Implementation

1405B - Array Cancellation

Author: hugopm

Tutorial

Implementation

1405C - Balanced Bitstring

Author: Kuroni

Tutorial

Implementation

1405D - Tree Tag

Author: Maripium

Tutorial

Implementation

1405E - Fixed Point Removal

Author: hugopm

Tutorial

Implementation

1404D - Game of Pairs

Author: Ari

Tutorial

Implementation

1404E - Bricks

Author: 1-gon

Tutorial

Implementation

• +421

By 1-gon, history, 6 months ago,

Warning: The following contains graphic depictions of geometry, precision errors, and degenerate cases. Viewer discretion is advised.

### Prerequisites

I assume the reader is familiar with:

• 2D Convex Hulls
• 3D Vector Operations (dot and cross products)

### Introduction

Recall that in the 2D convex hull problem, you are given a set of 2D points, and you must compute the smallest convex polygon containing all the given points. By convex, we mean that for any two points $A$ and $B$ inside the polygon, the entire line segment $AB$ is also inside the polygon.

The problem in 3D is completely analogous. You are given $n$ 3D points, and you must compute the smallest convex polyhedron containing all the given points. Similarly, a polyhedron is convex if for any two points $A$ and $B$ inside the polyhedron, the line segment $AB$ is also inside the polyhedron.

In the 2D case, it is more obvious what the output is. We can simply output a circular list of vertices on the boundary of the polygon. But how do we represent a polyhedron? Well, we can represent it as a list of triangular faces. A non-triangular face can always be divided into triangles, so this requirement isn't limiting.

In the 2D case, there are some degenerate cases that are easy to miss if you're not careful. For example, if there are 3 collinear points, the polygon might have two adjacent sides of the same slope, or you might combine them into one segment. Or if two points have the same $x$ coordinate, you need to handle this carefully when sorting the points by $x$ coordinate. Or if all the points are on the same line, then the convex hull isn't even a non-degenerate polygon!

Now, degenerate cases are bad enough with two dimensions. When you enter the third dimension, it only gets worse. What if four points lie on the same plane? Since we are requiring triangular faces, we could triangulate this in multiple ways. Or maybe we could choose to have one triangle of three points and the forth point lies inside this triangle, so we ignore it. What if all the points are on the same line? Or on the same plane even? Then the convex hull isn't a non-degenerate polyhedron. I will ignore such degenerate cases for now, and revisit them when applicable.

### Brute Force Algorithm in $O(n^4)$

Suppose that $n$ is very small, and we are guaranteed that no four points are coplanar. Then how can we compute the 3D convex hull in the dumbest way possible?

We could simply consider every triplet of three points $(\vec{a}, \vec{b}, \vec{c})$, and check if they create a triangular face on the convex hull. In order for it to be a face, the remaining points must "see" the same side of the triangle. In other words, if we consider the plane containing this triangle, the remaining points should lie on the same side of the plane. To compute which side of a plane a point $\vec{p}$ is on, we can first take a vector $\vec{q}=(\vec{b}-\vec{a})\times (\vec{c}-\vec{a})$ orthogonal to the plane.

Then the sign of $(\vec{p}-\vec{a})\cdot \vec{q}$ tells us the side of the plane. In particular, a result of $0$ tells us that $\vec{p}$ lies on the plane.

For each triplet, we perform this check with all points, so the total time complexity is $O(n^4)$. Because of its simplicity, this should be the approach when the constraints allow it. And by examining the brute force case, we learned how to perform the most fundamental operation in any 3D convex hull algorithm: checking which side of a plane a point is on.

### Incremental Algorithm in $O(n^2)$

Now we want a more practical algorithm. The strategy of the incremental algorithm is to build the convex hull for the first $i$ points, as $i$ goes from $1$ to $n$. All we have to do is figure out how to update the convex hull in order to accommodate one new point.

Let's start by making an analogy with the 2D convex hull. Suppose we currently have the convex hull of the first $i-1$ points, and we wish to add the $i$-th point. Well, if the point is already inside the hull, we should do nothing. Otherwise, let's pretend we are looking at the polygon from the perspective of the $i$-th point. We should delete all line segments it can see, and add two new line segments: one from the new point to each of the extreme points.

A similar thing happens in the 3D case. If a point is already inside the polyhedron, we simply do nothing. Otherwise, we delete all faces the new point can see. But what's less obvious is what we should add. Well, we've left the polyhedron with a connected set of faces removed. This exposes a cycle of edges. For each of these exposed edges, we should create a new face with the new point and that edge. Effectively, we create a cone of faces to repair the opening.

Now, let's analyze the time complexity. For each iteration, we process all faces of the hull in that iteration. And it can be proven that the number of faces is at most $O(n)$. For the proof, we use Euler's Formula. It states that for any polyhedron with $V$ vertices, $E$ edges, and $F$ faces, $V-E+F=2$. All faces are triangles, so we can substitute $E=3F/2$ since each face has $3$ edges, and we count each edge twice for the $2$ faces it touches. Then we have $V-F/2=2$ and $F=2V-4=O(n)$. Since we process $O(n)$ faces in $O(n)$ iterations, the overall time complexity is $O(n^2)$.

Implementation

### Clarkson-Shor Algorithm in $O(n\log n)$

There exist deterministic algorithms to compute the 3D convex hull in $O(n\log n)$ time. Most famously, there is a divide-and-conquer algorithm that solves it, but it is notoriously difficult to implement correctly. But don't lose hope! There is also a randomized algorithm based on the same incremental strategy, which takes $O(n\log n)$ time in expectation if the points are added in random order.

Unfortunately, we can't just shuffle the points, run the usual incremental algorithm, and call it a day. In order to achieve $O(n\log n)$, we should eliminate extra work of processing faces invisible to the new point. How can we do this? Well, before we checked for each point, which faces it can see. Instead, we will maintain for each point a list of faces it can see. Then when a face is created, we add it to the lists of all points that will be able to see it.

"Wait a minute," I hear you ask, "isn't this still $O(n^2)$ because you still process the same thing for each face-point pair, just in a different order?" The answer is yes, we're still in $O(n^2)$ territory. But, using some cleverness we can avoid checking with every future point every time. The key idea is that a new face $F$ is attached to a preexisting edge $e$, and a point $p$ can see $F$ only if it was able to see one of the two faces of $e$. Instead of checking with all future points, we will only check the points in the lists associated with these two faces. Unfortunately, this requires more work to implement. This time, we need to start caring about how the faces touch each other in order to find these two lists.

### Precision Errors and Degenerate Cases

The only potential source for precision error is our check of which side of a plane a point is on. If all coordinates are integers, then our computations will stay as integers. If it overflows on 64-bit integer types, we can convert to floating points for computations, and set $\epsilon=0.5$. Note that if the coordinates are at most $X,Y,Z$ in absolute value (in the $x$, $y$, and $z$ directions, respectively), our computations are on the order of $XYZ$. Use this to determine whether it will overflow on integers or find what you should set $\epsilon$ to. Or just try every possible $\epsilon$ until it works.

To handle degenerate cases, we should make sure that the first $4$ points are not coplanar. If all the $n$ points do not lie in the same plane, we can find $4$ such points and move them to the beginning. Once this is taken care of, the only real degenerate case remaining is when we need to decide if a face is visible to a point in the same plane. I made the decision to consider the face invisible in such a case. This ensures that the algorithm won't unnecessarily subdivide faces. However, it is still possible that the algorithm can have non-unique output depending on the order the points are processed. This is inevitable since there is not a unique way to triangulate faces in degenerate cases. If we want to, though, we can combine adjacent coplanar faces afterward.

To my knowledge, the Clarkson-Shor algorithm doesn't make any guarantees about $O(n\log n)$ performance when there are degenerate cases. I think by considering coplanar faces invisible, we are safe. But if anyone has a nasty test case against it, I'm interested to hear it.

Here is my implementation of the $O(n\log n)$ algorithm with full handling of degenerate cases (I hope). If anyone finds bugs or ways to improve the efficiency and/or readability of this implementation, please let me know!

Implementation

### Application: Delaunay Triangulation and Voronoi Diagrams

Suppose we have $n$ 2D points $p_1,\ldots,p_n$. We may want to answer queries of the form "Which point is closest to $q_i$?" for $m$ points $q_1,\ldots,q_m$.

The Voronoi diagram divides the plane into $n$ regions in a way that can answer these kinds of queries. It is defined so that the region corresponding to the point $i$ consists of all points closer to $p_i$ than any other of the $n$ points. That is, it's the region of points whose nearest neighbor is $p_i$. The Voronoi diagram can be represented by $O(n)$ line segments that border the regions (some line segments will extend to a point at infinity). We can answer our queries by sorting all line segments and queries, and performing a sweep line.

The dual of the Voronoi diagram is called the Delaunay Triangulation. It is the graph that connects two points if their Voronoi cells share a boundary edge. Conversely, the points of a Voronoi diagram are the circumcenters of Delaunay triangles, and an edge in the Voronoi diagram connects the circumcircles of two adjacent Delaunay triangles.

The Delaunay triangulation has several nice properties that come in handy. Most notably, the Euclidean minimum spanning tree consists of a subset of Delaunay edges. A nice consequence of implementing 3D convex hull is that we get Delaunay triangulation for free. We can simply map each point $(x,y)$ into a 3D point $(x,y,x^2+y^2)$. Then the downward-facing triangles of the 3D convex hull are precisely the Delaunay triangles. The proof is left as an exercise to the reader.

### Practice Problems

• +393

By 1-gon, history, 7 months ago,

I hope you enjoyed the contest! I will add implementations later.

1382A - Common Subsequence

Tutorial

Implementation

1382B - Sequential Nim

Tutorial

Implementation

1382C1 - Prefix Flip (Easy Version)

Tutorial

1382C2 - Prefix Flip (Hard Version)

Tutorial

1382D - Unmerge

Tutorial

Implementation

1382E - Mastermind

Tutorial

Implementation

1381D - The Majestic Brown Tree Snake

Tutorial

Implementation

1381E - Origami

Tutorial

Implementation

• +497

By 1-gon, history, 7 months ago,

Hello, Codeforces!

I'm very glad to invite you to Codeforces Round #658 (Div. 1) and Codeforces Round #658 (Div. 2). This contest will take place on Jul/21/2020 17:35 (Moscow time). In both divisions, you will have 2 hours to solve 5 problems (and one subtask). The score distribution will be announced closer to the start of the round.

Huge thanks to:

I've worked hard to ensure the pretests are short and the statements are strong. Remember to only read the problems that you can solve, and may you have the best of luck!

Because I know you all have so many questions, I have compiled an FAQueue

• Q: Is it rated?
• A: Yes

UPD Here is the score distribution:

Div. 2: 500 — 1250 — (1000 + 1000) — 2250 — 3000

Div. 1: (500 + 500) — 1500 — 2000 — 2500 — 3000

UPD Editorial

UPD I filled in the answer to the FAQ. Also, congrats to the winners!

Div. 2:

Div. 1:

• +2085

By 1-gon, history, 10 months ago,

Hello, Codeforces!

I'm very glad to invite you to Codeforces Round #639 (Div. 1) and Codeforces Round #639 (Div. 2). This contest will take place on May/06/2020 17:35 (Moscow time). In both divisions, you will have 2 hours 15 minutes to solve 6 problems. The score distribution will be announced closer to the start of the contest.

Of course, this round would not be possible without the help of many people, who I'd like to thank:

I've done my best to write clear problem statements and strong pretests. Don't forget to read all problems, and I hope you enjoy the round. Good luck!

Because I know you all have so many questions, I have compiled an FAQ:

• Q: Is it rated?
• A: Yes.

UPD

Here is the score distribution.

Div. 1: 500 — 1000 — 1500 — 1750 — 2500 — 2500

Div. 2: 500 — 1000 — 1500 — 2000 — 2500 — 2750

UPD Unfortunately, this contest is delayed due to problems in the Codeforces system. It is temporarily scheduled, but I will update this announcement when a final decision on scheduling has been made. It is sad that my contest is delayed, but let's be grateful that the issue was detected early instead of arising in the middle of the round. On the bright side, this may be the earliest announced score distribution in Codeforces history.

UPD The final decision on scheduling is to keep it as is. The contest will take place on May/06/2020 17:35 (Moscow time).

UPD Unfortunately due to the long queue and other issues, the contest is unrated. There was also a bug that gave us trouble in answering your questions. I'm sorry if you asked a question that went unanswered for a long time. I promise we did our best to answer as many questions as we could! Despite all the problems, I hope you enjoyed the problemset!

UPD Editorial

• +2313

By 1-gon, history, 10 months ago,

1345A - Puzzle Pieces

Tutorial

1345B - Card Constructions

Tutorial

1345C - Hilbert's Hotel

Tutorial

1345D - Monopole Magnets

Tutorial

1345E - Quantifier Question

Tutorial

1345F - Résumé Review

Tutorial

1344E - Train Tracks

Tutorial

1344F - Piet's Palette

Tutorial