I will describe a type of constructive problem that comes up fairly regularly on programming contests, especially Codeforces. We are given what I will call a "minimizing construction" problem, one where we are asked to construct a least such object that satisfies certain properties. One common prototype is that we are given a structure (e.g., a tree, an array, etc.) and a set of operations we can perform on the structure, and we wish to find a sequence of operations of minimal length such that the structure satisfies a property . There are other common examples, but we'll use this language of operations for now. Sometimes the problem asks to output the entire construction (i.e., the list of operations to perform); other times we just need to print out the size of the construction.
This is clearly a very general class of problems, but I want to share a common theme that shows up in the solutions of some of these problems that makes them particularly difficult to approach if you're not already aware of it. The idea is to impose an upper bound on the size of a construction by demonstrating that the problem can always be solved in at most (typically, a very small number) operations. This then reduces the problem to checking whether the problem can be solved in operations, which typically involves employing some additional optimizations to perform these checks efficiently.
I've compiled an (incomplete) list of problems that demonstrate this idea. You are welcome to try to and solve them now, with the mindset that you are searching for these upper-bound constructions. I will outline solutions to some of these and provide additional comments below.
ANDfinity
This problem is a prototypical example of this idea. The first step in these kinds of problems is to play around with the operations and gain some intuition for how they work. In this case, we can observe that it's quite "easy" for nodes to be connected-- in other words, in order for and to be in different components, not only must they have entirely disjoint sets of bits, their respective components must also have disjoint set of bits. Since there are only up to distinct bits, for sufficiently large , most random arrays are likely to be form connected graphs. [1]
[1] | Okay, citation needed. But this claim is not hard to believe. |
From here, it's relatively simple to come up with various constructions that place upper bounds on the number of operations required. For example, one approach is to ensure that all numbers have a common bit, and this is most easily done by making all of the numbers odd. Clearly, this takes at most operations.
However, we can do better. A simple improvement is to observe that since there are at most distinct bits, there are at most connected components, and to make the graph connected, it suffices to ensure that at least one number in each component is odd (since making an even number odd doesn't disturb any of the other bits). For example, consider the array . Thus we can reduce this upper bound to .
We can observe that in many cases, one operation can connect more that just components. In particular, in our example above with powers of , if we subtract from , we get the binary number which will connect all the other numbers to form a single connected component. This observation mostly works in the general case--for each number, compute its least significant bit, and for each component, find the vertex with the minimum LSB. Then if we take the component with the maximum of all minimal LSBs and subtract from any number in this component, this number will be of the form and connect all the other components, since every other component has some number with a smaller LSB.
The only problem with this construction is that subtracting may disconnect the vertex from its own connected component --for example, if we subtract from , it is no longer connected to . This is only a problem if has more than one vertex, and all the other values are even. In this case, we can fix this by simply adding to any other vertex in .
Thus, we can for sure solve this problem in just operations. To check if we can do any better, we can first check if we don't need any operations at all (i.e., the graph is connected to begin with), or if we can connect the graph in just operation. Checking if the graph is originally connected is trivial, but checking if just operation is sufficient is tricker, since there are up to operations to check, and performing a floodfill each time is . One approach is to observe that we can convert the connectivity requirement to a graph of bits, where we connect to if there is a number that contains both and . This graph is small enough to rebuild and floodfill for each operation.
Observe that the solution decomposes as two fairly disjoint parts--first creating a construction that always requires at most operations, and then coming up with a method of checking if just operation is sufficient efficiently. This kind of separation of concerns is quite common in these types of problems.
Misha and Paintings
Let be the initial number of distinct colors. First, if we have too few colors to begin with (i.e., ), clearly the best we can do is add new colors one by one until we have reached the desired amount , so our answer is simply . Otherwise, our goal is to reduce the total number of distinct colors by .
It turns out that operations is always enough. The general idea is that we can imagine creating a submatrix starting at the top-left corner and gradually increasing its side length. At some length , it will have covered just under colors, and if we increasing the length anymore, it will have covered more than colors. Then we can always add another square with its bottom-right corner at and increase its length until it has covered the desired number of colors. This comment helps gives some visual intuition for why this construction works.
This line of reasoning is a bit delicate, but fortunately, we only have to output the minimum number of operations needed. Now all that is left is to check whether we can get distinct colors in just operation (since , clearly at least one operation is needed). Since is fairly small, the constraints admit a cubic solution, where we iterate over all side lengths and check whether there is a valid submatrix of length in . For each color, we can mark out a region where submatrices of length will completely cover all squares of this color, and then use prefix sums to check if a submatrix will cover exactly or colors (we can cover an extra color since we can choose whether to give the submatrix an old or new color).
In this case, I think coming up with this construction is substantially harder, and part of the reason is that there is not much indication that the answer when should be at most , except for possibly a vague notion that reducing the number of colors should be "easier" then introducing new colors. This is why you should keep this idea in mind when approaching problems that seem to give you a lot of freedom in how you select your operations.
Quadratic Set
We can think of this problem as trying to minimize the size of a subset of to delete to make the resulting set quadratic. It is useful to write a brute force to examine small cases, and from here it is not hard to conjecture that minimum size of the removed set seems to be small, usually at most .
We can prove this by considering and trying to rewrite it into the form , where is a product of a small number of factorials. If is a multiple of , then we can always make the set quadratic by removing . Similarly, if , then we can remove and . So if is even, we have to remove at most numbers. If is odd, we have to remove at most numbers, since we can always reduce to the even case by removing . Therefore, this problem reduces to checking whether we can remove , , or numbers to make the set quadratic.
To check whether a set is quadratic, we can consider the prime factorization of the product of the set and check that each prime occurs an even number of times. This parity condition leads to think of using xor to count the parity of the number of occurences of a prime. Let be the xor of the prime factorization of . Then if is a square, . However, the converse does not necessarily hold, since it's possible that a nomempty set of primes could have an xor of . This issue is actually a common problem when working with xor (see this problem).
One trick to work around this issue is to assign each prime a random hash, and xor these hashes instead of the primes themselves. Then the probability of a collision becomes vanishingly small. We can then precompute for , and use these values to check whether or .
You can refer to the editorial for more details.
Conclusion
These problems are just a few instances of this technique; I'm sure you can find many more examples in other contests as well. I hope you found this useful!