Heap and Hashmaps with print statements for visualization
Approach
Use a max heap to keep track of tasks (which sorts by decreasing priority, taskId).
Use a hashmap to keep track of the latest priority and userId for a
# Approach
1. Use a max heap to keep track of tasks (which sorts by decreasing priority, taskId).
2. Use a hashmap to keep track of the latest priority and userId for a given task, which lets you pop from the heap tasks that have expired (that way, we don't have to pop with a given index in the heap)

# Complexity
Let N = # of operations
- Time complexity:
add: O(LogN)
edit: O(logN)
rmv: O(1)
execTop: O(logN) amortized
We heappop only whatever is added to the heap (which is N), so at
the end, if we perform this operation N times, the total complexity would be NLogN.

- Space complexity:
O(N)

# Code
```python3 []
class TaskManager:
    def __init__(self, tasks: List[List[int]]):
        self.tasks = [] # maxheap
        self.taskToLatestPrio = {}
        self.taskToUserId = {}

        for userId, taskId, priority in tasks:
            self.add(userId, taskId, priority)

    def add(self, userId: int, taskId: int, priority: int) -> None:
        #print("Added task: user={},task={},priority={}".format(userId,taskId,priority))
        heapq.heappush(self.tasks, (-priority, -taskId, userId))
        self.taskToLatestPrio[taskId] = priority
        self.taskToUserId[taskId] = userId
        

    def edit(self, taskId: int, newPriority: int) -> None:
        #print("Edited taskId={} with newPriority={}".format(taskId, newPriority))
        self.taskToLatestPrio[taskId] = newPriority
        userId = self.taskToUserId[taskId]
        heapq.heappush(self.tasks, (-newPriority, -taskId, userId))

    def rmv(self, taskId: int) -> None:
        #print("Removed taskId={}".format(taskId))
        del self.taskToLatestPrio[taskId]
        del self.taskToUserId[taskId]
        

    def execTop(self) -> int:
        #print("------------------")
        #print("execTop")
        #print("self.taskToLatestPrio: ", self.taskToLatestPrio)
        #print("self.taskToUserId: ", self.taskToUserId)
        #print("self.tasks: ", self.tasks)
        if self.tasks == []:
            return -1
        priority, taskId, userId = heapq.heappop(self.tasks)
        priority *= -1
        taskId *= -1
        #print("priority={}, taskId={}, userId={}".format(priority,taskId,userId))
        while (taskId not in self.taskToLatestPrio) or (self.taskToLatestPrio[taskId] != priority) or (self.taskToUserId[taskId] != userId):
            # This taskId was removed already, or this task is not the latest, get another task
            #print("This taskId={} priority={} was removed already, pop another task".format(taskId, priority))
            if self.tasks == []:
                return -1
            priority, taskId, userId = heapq.heappop(self.tasks)
            priority, taskId, userId
            priority *= -1
            taskId *= -1
            #print("priority={}, taskId={}, userId={}".format(priority,taskId,userId))
        del self.taskToLatestPrio[taskId]
        del self.taskToUserId[taskId]
        #print("executeTop taskId={} priority={} userId={}".format(taskId, priority, userId))
        #print("------------------")
        return userId
```
['Hash Table', 'Heap (Priority Queue)', 'Python3']