Editorial for COCI '19 Contest 3 #4 Lampice
Submitting an official solution before solving the problem yourself is a bannable offence.
The first subtask can be solved in multiple ways. We will describe the solution using hashing which will be useful for the final subtask. Let's root the tree in a node . We are interested in all palindromic segments with one end in node . For every node , we will calculate two values:
Where is an array of colors on the path from to (, ) and is the value of the hash basis. These two values can be determined using a single DFS traversal of our tree. The path from to is a palindromic segment if holds. If we root the tree in every node and apply this procedure, we will cover all cases. The complexity of this algorithm is .
The second subtask is a classic, well-known problem of finding the longest palindromic substring which can be solved using Manacher's algorithm.
The solution for the third subtask is quite similar to the solution of the second subtask. The only difference is that we must apply Manacher's algorithm on a path between every pair of leaves.
We will begin our solution for the whole problem with the following observation: If there exists a palindromic segment of length , then there exists a palindromic segment of length . Therefore, we can use binary search on even and odd lengths to find the longest palindromic segment.
But, how do we check if a palindromic segment of fixed length exists in a given tree? To answer that question, we will first solve an easier version of that problem – we will check if there is a palindromic segment of a fixed length that contains the root of our tree, node .
In a rooted tree, a path between two nodes and consists of two tree branches, where the length of path on one branch is greater or equal to the length on the other branch. We will divide the path between and into three parts: path from to , path from to the root and path from the root to , where is chosen such that the lengths and are equal. Note that is uniquely defined by the node , since we only care about palindromes of fixed size. In order to check whether the path from to is a palindromic segment, it is enough to check the following:
- (1) Path from to is a palindrome (depicted in red)
- (2) Colors from to are the same as colors from to (depicted blue).
For each node of the tree, we will calculate the values and in advance in the same manner as it was described in the solution of the first subtask. The first check is simply done by comparing and . The second check can be done by comparing the values:
- (3) , where is the parent of node and is the depth of node .
Now we know how to efficiently check whether the path from to is a palindromic segment, but that is still not enough because there are such pairs . Let's try to observe the problem from another angle: If we fix , is there (at least) one that satisfies all the conditions? Let be a set of hashes in which we will insert values of all nodes of our tree. Then, for a fixed , we can simply check condition (1) and we can check condition (3) by finding the corresponding value in . We can assume that the complexity of all operations with is constant if we use a hash table (std::unordered_set
in C++, for example). The complexity of the whole check is therefore .
Note: during implementation you need to make sure that the lowest common ancestor of nodes in and is the root node .
Now that we know whether a palindromic segment of certain length which passes through the root exists, we can use centroid decomposition. If we perform this check for every decomposed subtree with the centroid as a root, we will cover all the cases. With centroid decomposition and a binary search, we end up with an algorithm of complexity .
Comments