<p>You are given <code>n</code>​​​​​​ tasks labeled from <code>0</code> to <code>n - 1</code> represented by a 2D integer array <code>tasks</code>, where <code>tasks[i] = [enqueueTime<sub>i</sub>, processingTime<sub>i</sub>]</code> means that the <code>i<sup>​​​​​​th</sup></code>​​​​ task will be available to process at <code>enqueueTime<sub>i</sub></code> and will take <code>processingTime<sub>i</sub></code><sub> </sub>to finish processing.</p>

<p>You have a single-threaded CPU that can process <strong>at most one</strong> task at a time and will act in the following way:</p>

<ul>
	<li>If the CPU is idle and there are no available tasks to process, the CPU remains idle.</li>
	<li>If the CPU is idle and there are available tasks, the CPU will choose the one with the <strong>shortest processing time</strong>. If multiple tasks have the same shortest processing time, it will choose the task with the smallest index.</li>
	<li>Once a task is started, the CPU will <strong>process the entire task</strong> without stopping.</li>
	<li>The CPU can finish a task then start a new one instantly.</li>
</ul>

<p>Return <em>the order in which the CPU will process the tasks.</em></p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<pre>
<strong>Input:</strong> tasks = [[1,2],[2,4],[3,2],[4,1]]
<strong>Output:</strong> [0,2,3,1]
<strong>Explanation: </strong>The events go as follows: 
- At time = 1, task 0 is available to process. Available tasks = {0}.
- Also at time = 1, the idle CPU starts processing task 0. Available tasks = {}.
- At time = 2, task 1 is available to process. Available tasks = {1}.
- At time = 3, task 2 is available to process. Available tasks = {1, 2}.
- Also at time = 3, the CPU finishes task 0 and starts processing task 2 as it is the shortest. Available tasks = {1}.
- At time = 4, task 3 is available to process. Available tasks = {1, 3}.
- At time = 5, the CPU finishes task 2 and starts processing task 3 as it is the shortest. Available tasks = {1}.
- At time = 6, the CPU finishes task 3 and starts processing task 1. Available tasks = {}.
- At time = 10, the CPU finishes task 1 and becomes idle.
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> tasks = [[7,10],[7,12],[7,5],[7,4],[7,2]]
<strong>Output:</strong> [4,3,2,0,1]
<strong>Explanation</strong><strong>: </strong>The events go as follows:
- At time = 7, all the tasks become available. Available tasks = {0,1,2,3,4}.
- Also at time = 7, the idle CPU starts processing task 4. Available tasks = {0,1,2,3}.
- At time = 9, the CPU finishes task 4 and starts processing task 3. Available tasks = {0,1,2}.
- At time = 13, the CPU finishes task 3 and starts processing task 2. Available tasks = {0,1}.
- At time = 18, the CPU finishes task 2 and starts processing task 0. Available tasks = {1}.
- At time = 28, the CPU finishes task 0 and starts processing task 1. Available tasks = {}.
- At time = 40, the CPU finishes task 1 and becomes idle.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>tasks.length == n</code></li>
	<li><code>1 &lt;= n &lt;= 10<sup>5</sup></code></li>
	<li><code>1 &lt;= enqueueTime<sub>i</sub>, processingTime<sub>i</sub> &lt;= 10<sup>9</sup></code></li>
</ul>

class Solution {
private:
    struct Task {
        unsigned long long enque_time;
        unsigned long long processing_time;
        int idx;
    };

    class TaskHeap {
    private:
        vector<Task>& heap;
        bool (*is_smaller) (Task&a, Task& b);

        void sift_down(int root) {
            while (true) {
                int next = root;
                int left_child = 2*root + 1;
                int right_child = 2*root + 2;

                if (left_child < heap.size() && is_smaller(heap[left_child], heap[next])) {
                    next = left_child;
                }

                if (right_child < heap.size() && is_smaller(heap[right_child], heap[next])) {
                    next = right_child;
                }

                if (root == next)
                    break;

                swap(heap[next], heap[root]);
                root = next;
            }
        }

        void sift_up(int root) {
            while (root > 0) {
                int parent = (root - 1) / 2;
                if (is_smaller(heap[root], heap[parent])) {
                    swap(heap[root], heap[parent]);
                    root = parent;
                }
                else {
                    break;
                }
            }
        }

    public:
        TaskHeap(vector<Task>& buffer, bool (*comparison)(Task& a, Task&b)) : heap(buffer) {
            is_smaller = comparison;
            for (int r = (heap.size() / 2) - 1; r >= 0; --r) {
                sift_down(r);
            }
        }

        Task& get_next() {
            return heap[0];
        }

        void pop() {
            swap(heap[0], heap[heap.size() - 1]);
            heap.pop_back();
            sift_down(0);
        }

        void push(Task& t) {
            heap.emplace_back(t.enque_time, t.processing_time, t.idx);
            sift_up(heap.size() - 1);
        }

        bool is_empty() {
            return heap.empty();
        }
    };

public:
    vector<int> getOrder(vector<vector<int>>& tasks) {
        vector<int> result;

        auto sort_by_time = [] (Task& a, Task& b) { return (a.processing_time < b.processing_time || a.processing_time == b.processing_time && a.idx < b.idx); };
        auto sort_by_enque_time = [] (Task& a, Task& b) { return (a.enque_time < b.enque_time); };

        vector<Task> buffer_1;
        for (int i = 0; i < tasks.size(); ++i)
            buffer_1.push_back({ (unsigned long long)tasks[i][0], (unsigned long long)tasks[i][1], i });

        TaskHeap enque_heap(buffer_1, sort_by_enque_time);
        unsigned long long time = 0;

        vector<Task> buffer_2;
        TaskHeap time_heap(buffer_2, sort_by_time);

        while (result.size() < tasks.size()) {
            if (time_heap.is_empty()) {
                int earliest_time = -1;

                while(!enque_heap.is_empty()) {
                    Task& t = enque_heap.get_next();
                    if (earliest_time == -1) {
                        earliest_time = t.enque_time;
                        time_heap.push(t);
                        enque_heap.pop();
                    }
                    else if (t.enque_time == earliest_time || t.enque_time <= time) {
                        time_heap.push(t);
                        enque_heap.pop();
                    }
                    else {
                        break;
                    }
                }

                if (time < earliest_time)
                    time = earliest_time;
            }
            else {
                while(!enque_heap.is_empty()) {
                    Task& t = enque_heap.get_next();
                    if (t.enque_time <= time) {
                        time_heap.push(t);
                        enque_heap.pop();
                    }
                    else {
                        break;
                    }
                }
            }

            Task process = time_heap.get_next();
            time_heap.pop();

            time += process.processing_time;
            result.push_back(process.idx);
        }

        return result;
    }
};

150555