Editorial for COCI '21 Contest 1 #4 Set
Submitting an official solution before solving the problem yourself is a bannable offence.
For the first subtask, it is sufficient to try out every triplet of cards and check whether it forms a set in .
For the second subtask, one should notice that if we choose two cards, the third card needed for a set is uniquely determined. Namely, for each position, if the corresponding characters in the first two cards are the same, then the third character has to be equal to them, and if they are different, the third character will be the remaining one. With time complexity , we can now try out every pair of cards and check whether there exists a third card which will form a set with them. Of course, we should first record in an array of size for each type of card if it shows up in the input.
From now on, we will assume that the characters in question are instead of , so that we can view each card as a number in base . Let's try to come up with a simple rule that associates a pair of characters with the third character needed for a set. In other words, we are looking for a rule that acts in the following way:
We can notice that , or equivalently, the characters form a set if and only if . Now let's make an analogy with the bitwise xor operation. This operation is denoted by and represents addition modulo with the digits in base . In this problem, we will consider addition modulo with the digits in base , which we will denote by . Having in mind the things mentioned above, three cards form a set if and only if , where the cards are thought of as numbers in base .
Let's fix some card and try to figure out how many pairs of cards and exist so that . For each individual , we can calculate this in so the total complexity is , but we will show a way to find the answer for all cards simultaneously in the complexity . If instead of the operation we had the operation , the problem could be solved by calculating the desired -convolution with fast multiplication of polynomials using FFT. In this problem, we will therefore try to modify this idea to calculate the -convolution.
The operations and are very similar and the -convolution is calculated in the same manner as the xor-convolution. Thus, what follows is a description of the modification of the 'fast Walsh–Hadamard transformation' (FWHT) to work modulo . More about this can be found on this Codeforces blog. (P.S. the day before the contest, another great blog on the topic appeared on Codeforces: link)
At a high level, the idea is the following:
- The given deck of cards is represented by a polynomial.
- Each term in the polynomial represents a certain type of card.
- The coefficients of the polynomial represent the number of times a card appears in the deck. In the beginning, all of the coefficients are either or .
- We will square the polynomial to get new coefficients which represent the result of the desired convolution. (For now, this corresponds to the operation).
- Before multiplying, we will convert the polynomial from coefficient form to point value form as a sequence of calculated values , which is more desirable for multiplication.
- The result of the multiplication should be converted back to coefficient form.
Regular multiplication of polynomials corresponds to the operation , that is . We would like to make a modification so that . We need to make two modifications:
- Addition should be done separately for each digit.
- Addition should be done modulo .
Problem 1 can be solved by introducing a polynomial with variables . For example, a pair of cards and (that is and ) is represented by a polynomial
Multiplication now corresponds to addition of the digits separately. Looking at each of the variables separately, the polynomial is of degree at most , but when squaring, the degree might grow larger.
To convert a polynomial (which remember has coefficients) to point-value form, we will calculate the value of the polynomial at different points. We will choose three values and calculate for each possible combination where , of which there are also . In the implementation, we will therefore have a transformation that converts a sequence of coefficients to a sequence of calculated values.
Since the product of two polynomials has more coefficients than the original polynomials, if we wanted to calculate the true product, we would have had to extend the polynomials to bigger powers, making the new coefficients . However, to solve problem 2, that is precisely what we will not do. When doing the inverse transformation which returns the coefficient form, we will purposefully demand that the result has coefficients. Additionally, for the values , we will choose the third roots of unity (both real and complex), that is the numbers , and , so that . The effect of these two things is that the coefficients of larger powers in the resulting product will get added to the coefficients of the smaller powers - precisely with the smallest power that is the same modulo (because of the choice of ). Thus, the powers will reduce modulo and we will get exactly the desired coefficients of the -convolution.
Let us illustrate this on an example with . For the polynomial, we take
which can also be written as
First, we will apply the transformation on each of the polynomials separately. Using the label , we have:
In this way, the sequence of coefficients
turns into a new list of coefficients, which we will label with
We would like to obtain a list that has the following values in order
What remains is to replace the triplets with new values, in the same manner as we did when calculating the values for .
For bigger values of , the process is analogous. In each of the iterations, we gradually transform the coefficients in the described way, each time jumping by a larger power of .
The inverse transformation is almost identical to the original, having the formula
It should be noted that in the implementation, there is no need to make calculations with complex numbers, whose real and imaginary parts are stored with a floating point type. Instead, we can notice that at each moment every number will be of the form , where and are whole numbers which fit in
long long int. When calculating, it is useful to keep in mind that and .