Editorial for COI '15 #1 Dijamant
Submitting an official solution before solving the problem yourself is a bannable offence.
The first step in solving this task is choosing which structure to use to store class names, in other words, to use to assign class names to ordinal numbers. One option is using map<string, int>
in C++. When we recognize a new valid declaration, we assign the next ordinal number to the class name. The first two subtasks come down to checking whether the classes are declared, so this is easily solved now. As an alternative, for example when using the C programming language which doesn't have this data structure, we can first read, save and sort the names of all declared classes. The index of individual classes can then be efficiently found using binary search. In any case, the complexity of one query to the structure is .
For the other two subtasks, we need to consider the paths in the directed graph that models the class hierarchy. Let's assume that we are considering a new declaration K : P1 P2 ... Pk ;
and first try to determine when a diamond is formed when adding it. Firstly, evidently it must hold . Furthermore, there must exist a path from to and from to so let and be classes on these paths immediately before . Since they are the last ones on the paths, classes and need to be one of the classes which inherits. It is crucial to notice that classes and cannot be derived one from another – in fact, when would, for example, be derived from then would be a diamond that already exists before adding the new declaration.
Therefore, when adding a new declaration, it is sufficient to check the following: Do two classes and exist among classes that inherits and which are not derived from each other, and that are both derived from some class ? Since we are processing declarations, for each valid declaration of class , we will calculate – the set of all ordinal numbers of classes which is derived from. A new declaration is not valid if there exist two classes and among classes that inherits so that it holds ( is not derived from ), ( is not derived from ) and ( and are both derived from some class ). This logic can be directly implemented by considering a pair of classes , which leads to an algorithm of the complexity , but can be optimized enough to score 100 points (for example, by using bitmasks).
The targeted solution efficiently checks whether classes and exist that satisfy the aforementioned conditions. Among other things, it is necessary to notice that if is derived from , then doesn't even need to be considered. For each declaration, we do the following:
- Sort the classes from the later declarations to the earlier ones.
- We maintain the set that corresponds to the union of all for classes that we have considered thus far. The set can be stored as an array of booleans.
- For each class ,
- If is already in , ignore it.
- Otherwise, add all elements from to . If we encounter an element already in while adding, then we found a diamond and the declaration is dismissed.
For each declaration, the number of processing steps is proportional to the total number of elements that are added to , so it is bound by . The total complexity is .
Comments