Here is the contest link.
https://codeforces.net/gym/590053/problem/A
A. A+ Student
B. Jo's Adventure
C. Binary Flip
D. Minimize the Distance
E. Weird and Ugly Monsters
The problem requires us to keep track of a group of monsters sitting in a circle, each with an ugliness score. Whenever two adjacent monsters have the same ugliness score, they merge into one, keeping the smaller index and doubling the ugliness score. Each newly added monster gets a unique increasing index, and after every insertion and all possible merges, we need to determine how many monsters remain in the table.
To handle this efficiently, we use a circular doubly linked list. Since each monster has a left and right neighbor, a doubly linked list allows easy movement in both directions. The circular part means that the last node's next pointer points to the first node, and the first node's previous pointer points to the last node. This makes it easier to manage the circle without needing extra checks. In addition to this, a dictionary is used to quickly find any monster's node by its index, so we don't have to search through the list every time.
When a new monster is added, it always appears to the right of the monster with the given index. Using the dictionary, we can find that monster's position immediately and insert the new one by updating a few pointers. After insertion, we check if the new monster can merge with the one on its left. If their ugliness scores are the same, they combine into one, with the remaining monster keeping the smaller index and doubling its ugliness score. This process repeats with the new left neighbor until no more merges are possible. After that, we check the right neighbor and continue merging in the same way. The merging keeps happening as long as there are adjacent monsters with the same ugliness score.
For time complexity, at the beginning, there is just one monster, and then we have $$$n$$$ insertions. Each insertion involves finding the right place in the list, which takes $$$O(1)$$$ time using the dictionary. Inserting a new monster also takes $$$O(1)$$$ time since it only involves pointer updates. After an insertion, we need to check for possible merges. In the worst case, each insertion may trigger up to $$$O(n)$$$ merges. However, since each adjacent monster pair can merge at most once, the total number of merges across all $$$n$$$ insertions will not exceed $$$n$$$. This means the overall time complexity for processing all insertions and merges is $$$O(n)$$$.
For space complexity, we need to store each monster as a node in the linked list, which takes $$$O(n)$$$ space. The dictionary also holds a reference to each monster, adding another $$$O(n)$$$ space. Since both structures grow at the same rate as the number of monsters, the overall space complexity is $$$O(n)$$$.
class ListNode:
def __init__(self, index, ugliness):
# Initialize a new monster node with an index and ugliness score.
self.index = index
self.ugliness = ugliness
self.next = None
self.prev = None
def merge_monsters(node, monster_map, count):
# Try to merge the node with its adjacent neighbors (left and right) as long as they have the same ugliness score.
while True:
merged = False
# Merge with the left neighbor if they have the same ugliness
if node.prev != node and node.ugliness == node.prev.ugliness:
left = node.prev
if node.index > left.index:
# Adjust the pointers to remove 'node' and merge with the left neighbor
left.next = node.next
node.next.prev = left
left.ugliness *= 2
count[0] -= 1 # Decrease the count of monsters after merging
node = left # Move 'node' to 'left' as it is now the active node
else:
# Adjust the pointers to remove 'left' and merge with 'node'
node.prev = left.prev
left.prev.next = node
node.ugliness *= 2
count[0] -= 1 # Decrease the count of monsters after merging
merged = True
# Merge with the right neighbor if they have the same ugliness
elif node.next != node and node.ugliness == node.next.ugliness:
right = node.next
if node.index > right.index:
# Adjust the pointers to remove 'node' and merge with the right neighbor
node.prev.next = right
right.prev = node.prev
right.ugliness *= 2
count[0] -= 1 # Decrease the count of monsters after merging
node = right # Move 'node' to 'right' as it is now the active node
else:
# Adjust the pointers to remove 'right' and merge with 'node'
right.next.prev = node
node.next = right.next
node.ugliness *= 2
count[0] -= 1 # Decrease the count of monsters after merging
merged = True
# Exit the loop if no merges happened
if not merged:
break
def solve():
# Solve the problem for a single test case
num_insertions, initial_ugliness = map(int, input().split())
# Count is an array of 1 value because it is passed to the merge
# function and we need to modify the value in that function
count = [1] # Initialize count to 1 since we have 1 monster initially
head = ListNode(1, initial_ugliness)
head.next = head # Make the list circular by linking the head to itself
head.prev = head
monster_map = {1: head} # Map to track the monsters by their index
indices = list(map(int, input().split()))
ugliness_values = list(map(int, input().split()))
next_index = 2 # Start from the next available index for the new monsters
results = []
for i in range(num_insertions):
monster_index, ugliness_score = indices[i], ugliness_values[i]
monster = monster_map[monster_index]
# Create and insert the new monster into the linked list
new_monster = ListNode(next_index, ugliness_score)
next_index += 1
# Adjust the pointers to add the new monster
new_monster.next = monster.next
new_monster.prev = monster
monster.next.prev = new_monster
monster.next = new_monster
monster_map[new_monster.index] = new_monster # Add the new monster to the map
count[0] += 1 # Increase the count after adding a new monster
# Perform merges after insertion
merge_monsters(new_monster, monster_map, count)
# Store the current number of monsters after the insertion and merges
results.append(count[0])
print(*results)
t = int(input())
for _ in range(t):
solve()