We have n jobs, where every job is scheduled to be done from startTime[i] to endTime[i], obtaining a profit of profit[i].
You're given the startTime , endTime and profit arrays, you need to output the maximum profit you can take such that there are no 2 jobs in the subset with overlapping time range.
If you choose a job that ends at time X you will be able to start another job that starts at time X.
 
Example 1:

Input: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]
Output: 120
Explanation: The subset chosen is the first and fourth job. 
Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70.

Example 2:
 

Input: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]
Output: 150
Explanation: The subset chosen is the first, fourth and fifth job. 
Profit obtained 150 = 20 + 70 + 60.

Example 3:

Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]
Output: 6

 
Constraints:

1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4
1 <= startTime[i] < endTime[i] <= 10^9
1 <= profit[i] <= 10^4

```python

USER_FILENAME = "<user_code>"
import sys, json, builtins
import math, heapq, bisect, itertools, functools, collections, statistics, fractions, decimal, operator, array, random, re, string, os, pathlib
try:
    import resource as _res
except Exception:
    _res = None
from collections import Counter, defaultdict, deque, OrderedDict
from functools import lru_cache, reduce
from itertools import accumulate, permutations, combinations, product
from math import gcd, ceil, floor, sqrt
from decimal import Decimal
try:
    from typing import *
except Exception:
    pass
try:
    sys.setrecursionlimit(1_000_000)
except Exception:
    pass
try:
    import site as _site
    try:
        _sys_sites = _site.getsitepackages() if hasattr(_site, "getsitepackages") else []
        for _p in _sys_sites:
            if _p and _p not in sys.path:
                sys.path.append(_p)
    except Exception:
        pass
    try:
        _usp = _site.getusersitepackages()
        if isinstance(_usp, str):
            _usp = [_usp]
        for _p in _usp:
            if _p and _p not in sys.path:
                sys.path.append(_p)
    except Exception:
        pass
except Exception:
    pass

def _disabled_input(*args, **kwargs):
    raise RuntimeError("input() is disabled in callable mode")
builtins.input = _disabled_input

ns = {"__name__": "__judge__"}  # prevent if __name__ == "__main__" blocks
for _k, _v in list(globals().items()):
    if not _k.startswith("_"):
        ns[_k] = _v
_CODE = "class Solution:\n    def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int:\n        '''\n        Refer to Eva Tardos's 'Algorithm Design' - 6.1 Weighted Interval Scheduling\n        \n        First post in the discuss section explains the same approach\n        \n        Steps\n        ------\n        1. Sort and order jobs by the end time - O(n logn)\n        \n        2. Build a most_recent array with most_recent[j] being the index of the most recent disjoint job - O(n logn)\n           ** use binary search to build this array ** \n           \n        3. Calculate profit for each position using the relation - O(n)\n           ** dp_profits[j] = max(profits[j] + dp_profits[previous[j]], dp_profits[j-1]) **\n        '''\n        \n        # zip and order jobs by end time\n        jobs = sorted(zip(startTime, endTime, profit), key=lambda x: x[1])\n        \n        '''\n        for each job in jobs find the most recent disjoint\n        this is nothing but finding the index of the floor of each start time in the sorted end times\n        i.e. for each start time, finding the index of greatest end time less than or equal to start time\n        \n        if the floor is not present the value returned would be -1\n        '''\n        def binary_search(val: int):\n            '''\n            Try doing a dry run with test cases before implementing\n            \n            Always choose one test value in the left of mid, one in the right and one for each boundar condition.\n            Refer to vscode playground floor.py\n            '''\n            nonlocal jobs\n            \n            # boundary conditions - note job = (start_time, end_time, profit)\n            if val < jobs[0][1]:\n                return -1\n            if val >= jobs[-1][1]:\n                return len(jobs) - 1\n            \n            start, end = 0, len(jobs) - 1\n            \n            while start < end - 1:\n                mid = start + (end - start) // 2\n                \n                if jobs[mid][1] < val:\n                    start = mid\n                elif jobs[mid][1] > val:\n                    end = mid -1\n                else:\n                    return mid\n            \n            return end if jobs[end][1] <= val else start\n        \n        most_recent = [0] * len(jobs)\n        \n        for i, job in enumerate(jobs):\n            most_recent[i] = binary_search(job[0])\n        \n        '''\n        Calculate profit for each job using recurrence relation and return the last value.\n        \n        \n        '''\n        dp_profits = [0] * len(jobs)\n        for i, job in enumerate(jobs):\n            most_recents_profit = 0 if most_recent[i] == -1 else dp_profits[most_recent[i]]\n            prev_profit = 0 if i == 0 else dp_profits[i-1]\n            \n            dp_profits[i] = max(job[2] + most_recents_profit, prev_profit)\n        \n        return dp_profits[-1]"
_co = compile(_CODE, USER_FILENAME, "exec")
exec(_co, ns, ns)

_target = None
_fn_name = 'jobScheduling'
if _fn_name:
    if "Solution" in ns:
        try:
            _obj = ns["Solution"]()
            if hasattr(_obj, _fn_name):
                _target = getattr(_obj, _fn_name)
        except Exception:
            _target = None
    if _target is None and _fn_name in ns and callable(ns[_fn_name]):
        _target = ns[_fn_name]
# Fallback: if there's exactly one user-defined function, use it
if _target is None:
    _cands = [v for k, v in ns.items() if callable(v) and getattr(v, "__module__", "") in ("__judge__", "__main__")]
    _user_funcs = [c for c in _cands if getattr(c, "__name__", "").startswith("_") is False]
    if len(_user_funcs) == 1:
        _target = _user_funcs[0]
if _target is None:
    raise RuntimeError(f"Could not resolve callable '{_fn_name}'")

_raw = sys.stdin.read().strip()
if _raw:
    _parsed = json.loads(_raw)
    if isinstance(_parsed, dict):
        _kwargs = _parsed.get("kwargs", {})
        _args = _parsed.get("args", [])
    else:
        _args, _kwargs = _parsed, {}
else:
    _args, _kwargs = [], {}
if not isinstance(_args, (list, tuple)):
    _args = [_args]
_res = _target(*_args, **_kwargs)
try:
    sys.stdout.write(repr(_res) + "\n")
except Exception:
    pass

```

Measured memory_bytes: 6840