You are given two 0-indexed integer arrays servers and tasks of lengths n​​​​​​ and m​​​​​​ respectively. servers[i] is the weight of the i​​​​​​th​​​​ server, and tasks[j] is the time needed to process the j​​​​​​th​​​​ task in seconds.

Tasks are assigned to the servers using a task queue. Initially, all servers are free, and the queue is empty.

At second j, the jth task is inserted into the queue (starting with the 0th task being inserted at second 0). As long as there are free servers and the queue is not empty, the task in the front of the queue will be assigned to a free server with the smallest weight, and in case of a tie, it is assigned to a free server with the smallest index.

If there are no free servers and the queue is not empty, we wait until a server becomes free and immediately assign the next task. If multiple servers become free at the same time, then multiple tasks from the queue will be assigned in order of insertion following the weight and index priorities above.

A server that is assigned task j at second t will be free again at second t + tasks[j].

Build an array ans​​​​ of length m, where ans[j] is the index of the server the j​​​​​​th task will be assigned to.

Return the array ans​​​​.

&nbsp;
Example 1:

Input: servers = [3,3,2], tasks = [1,2,3,2,1,2]
Output: [2,2,0,2,1,2]
Explanation: Events in chronological order go as follows:
- At second 0, task 0 is added and processed using server 2 until second 1.
- At second 1, server 2 becomes free. Task 1 is added and processed using server 2 until second 3.
- At second 2, task 2 is added and processed using server 0 until second 5.
- At second 3, server 2 becomes free. Task 3 is added and processed using server 2 until second 5.
- At second 4, task 4 is added and processed using server 1 until second 5.
- At second 5, all servers become free. Task 5 is added and processed using server 2 until second 7.

Example 2:

Input: servers = [5,1,4,3,2], tasks = [2,1,2,4,5,2,1]
Output: [1,4,1,4,1,3,2]
Explanation: Events in chronological order go as follows: 
- At second 0, task 0 is added and processed using server 1 until second 2.
- At second 1, task 1 is added and processed using server 4 until second 2.
- At second 2, servers 1 and 4 become free. Task 2 is added and processed using server 1 until second 4. 
- At second 3, task 3 is added and processed using server 4 until second 7.
- At second 4, server 1 becomes free. Task 4 is added and processed using server 1 until second 9. 
- At second 5, task 5 is added and processed using server 3 until second 7.
- At second 6, task 6 is added and processed using server 2 until second 7.


&nbsp;
Constraints:


	servers.length == n
	tasks.length == m
	1 &lt;= n, m &lt;= 2 * 105
	1 &lt;= servers[i], tasks[j] &lt;= 2 * 105


class Solution:
    def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]:
        servers_available = [(w, i) for i,w in enumerate(servers)]
        heapify(servers_available)
        tasks_in_progress = []
        res = []
        time = 0
        for j,task in enumerate(tasks):
            time = max(time, j)
            if not servers_available:
                time = tasks_in_progress[0][0]
            while tasks_in_progress and tasks_in_progress[0][0] <= time:
                heappush(servers_available, heappop(tasks_in_progress)[1])
            res.append(servers_available[0][1])
            heappush(tasks_in_progress, (time + task, heappop(servers_available)))
        return res
class Solution {
    public int[] assignTasks(int[] servers, int[] tasks) {

        PriorityQueue<int[]> availableServer = new PriorityQueue<int[]>((a, b) -> (a[1] != b[1] ? (a[1] - b[1]) : (a[0] - b[0])));
        for(int i = 0; i < servers.length; i++){
            availableServer.add(new int[]{i, servers[i]});
        }

        //int[[] arr,
        //arr[0] - server index
        //arr[1] - server weight
        //arr[2] - free time
        PriorityQueue<int[]> processingServer = new PriorityQueue<int[]>(
                (a, b) ->

                (
                    a[2] != b[2] ? a[2] - b[2] : // try to sort increasing order of free time
                    a[1] != b[1] ? a[1] - b[1] : // try to sort increasing order of server weight
                    a[0] - b[0] // sort increasing order of server index
                )
        );

        int[] result = new int[tasks.length];

        for(int i = 0; i < tasks.length; i++){

            while(!processingServer.isEmpty() && processingServer.peek()[2] <= i){
                int serverIndex = processingServer.remove()[0];
                availableServer.add(new int[]{serverIndex, servers[serverIndex]});
            }

            int currentTaskTimeRequired = tasks[i];

            int[] server;

            //when current task will free the server done
            int freeTime = currentTaskTimeRequired;

            if(!availableServer.isEmpty()){
                server = availableServer.remove();
                freeTime += i;
            }else{
                server = processingServer.remove();
                //append previous time
                freeTime += server[2];
            }

            int serverIndex = server[0];
            processingServer.add(new int[]{serverIndex, servers[serverIndex] ,freeTime});

            //assign this server to current task
            result[i] = serverIndex;
        }

        return result;

    }
}
class Solution {
public:
    vector<int> assignTasks(vector<int>& servers, vector<int>& tasks) {
        priority_queue<vector<long long>, vector<vector<long long>>, greater<vector<long long>>> free, busy; 
        for (int i = 0; i < servers.size(); ++i) {
            free.push(vector<long long>{servers[i], i});
        }
        vector<int> ans;
        long long time = 0;
        queue<int> task_queue;
        while (ans.size() < tasks.size()) { 
            // Bring all eligible tasks to task_queue.
            for (int i = ans.size() + task_queue.size(); i <= time && i < tasks.size(); ++i) {
                task_queue.push(tasks[i]);
            }
            // Bring all eligible servers to free queue.
            while (!busy.empty() && busy.top()[0] <= time) {
                auto& top = busy.top();
                free.push(vector<long long>{top[1], top[2]});
                busy.pop();
            }
            // If we have no servers, we cannot match until one of the servers becomes free.
            if (free.empty()) {
                time = busy.top()[0];
                continue;
            }
            while(!task_queue.empty() && !free.empty()) {
                // Now we just take the first task and first server as per defined priority and match the two.
                int task = task_queue.front(); 
                auto& top = free.top();
                ans.push_back(top[1]);
                busy.push(vector<long long>{time + task, top[0], top[1]});
                task_queue.pop();
                free.pop();
            }
            // Only increment time to receive new tasks once we have processed all tasks so far.
            if (task_queue.empty()) time++;
        }
        return ans;
    }
};
 var assignTasks = function(servers, tasks) {
    // create a heap to manage free servers.
    // free servers will need to be prioritized by weight and index
    const freeServers = new Heap((serverA, serverB) => (
        serverA.weight - serverB.weight || serverA.index - serverB.index
    ));

    // create a heap to manage used servers.
    // used servers will need to be prioritized by availableTime, weight and index
    const usedServers = new Heap((serverA, serverB) => (
        serverA.availableTime - serverB.availableTime || 
        serverA.weight - serverB.weight || 
        serverA.index - serverB.index
    ));
    
    // add all the servers into the free servers heap with the time it is available
    // being 0
    for (let i = 0; i < servers.length; i++) {
        freeServers.push({ weight: servers[i], index: i, availableTime: 0 })
    }
    
    const result = [];
    for (let i = 0; i < tasks.length; i++) {
        // find all the servers that are available and add them to the
        // free servers heap
        while (usedServers.size() && usedServers.peak().availableTime <= i) {
            freeServers.push(usedServers.pop());
        }
        // get the free server with the lowest weight or lower index
        // or the usedServer with the lowest start time.
        const server = freeServers.pop() || usedServers.pop();
        result.push(server.index);
        const availableTime = Math.max(i, server.availableTime);
        server.availableTime = availableTime + tasks[i];
        usedServers.push(server);
    }
    return result;
};

class Heap {

    /**
     * Create a Heap
     * @param {function} compareFunction - compares child and parent element
     * to see if they should swap.  If return value is less than 0 it will
     * swap to prioritize the child.
     */
    constructor(compareFunction) {
        this.store = [];
        this.compareFunction = compareFunction;
    }
    
    peak() {
        return this.store[0];
    }
    
    size() {
        return this.store.length;
    }
    
    pop() {
        if (this.size() < 2) {
            return this.store.pop();
        }
        const result = this.store[0];
        this.store[0] = this.store.pop();
        this.heapifyDown(0);
        return result;
    }
    
    push(val) {
        this.store.push(val);
        this.heapifyUp(this.size() - 1);
    }
        
    heapifyUp(child) {
        while (child) {
            const parent = Math.floor((child - 1) / 2);
            
            if (this.shouldSwap(child, parent)) {
                [this.store[child], this.store[parent]] = [this.store[parent], this.store[child]]
                child = parent;
            } else {
                return child;
            }
        }
    }
    
    heapifyDown(parent) {
        while (true) {
            let [child, child2] = [1,2].map((x) => parent * 2 + x).filter((x) => x < this.size());
            if (this.shouldSwap(child2, child)) {
                child = child2;
            }
            
            if (this.shouldSwap(child, parent)) {
                [this.store[child], this.store[parent]] = [this.store[parent], this.store[child]]
                parent = child;
            } else {
                return parent;
            }
        }
    }
    
    shouldSwap(child, parent) {
        return child && this.compareFunction(this.store[child], this.store[parent]) < 0;
    }
}
Process Tasks Using Servers