<p>You are given two <strong>0-indexed</strong> arrays <code>nums</code> and <code>cost</code> consisting each of <code>n</code> <strong>positive</strong> integers.</p>

<p>You can do the following operation <strong>any</strong> number of times:</p>

<ul>
	<li>Increase or decrease <strong>any</strong> element of the array <code>nums</code> by <code>1</code>.</li>
</ul>

<p>The cost of doing one operation on the <code>i<sup>th</sup></code> element is <code>cost[i]</code>.</p>

<p>Return <em>the <strong>minimum</strong> total cost such that all the elements of the array </em><code>nums</code><em> become <strong>equal</strong></em>.</p>

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

<pre>
<strong>Input:</strong> nums = [1,3,5,2], cost = [2,3,1,14]
<strong>Output:</strong> 8
<strong>Explanation:</strong> We can make all the elements equal to 2 in the following way:
- Increase the 0<sup>th</sup> element one time. The cost is 2.
- Decrease the 1<sup><span style="font-size: 10.8333px;">st</span></sup> element one time. The cost is 3.
- Decrease the 2<sup>nd</sup> element three times. The cost is 1 + 1 + 1 = 3.
The total cost is 2 + 3 + 3 = 8.
It can be shown that we cannot make the array equal with a smaller cost.
</pre>

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

<pre>
<strong>Input:</strong> nums = [2,2,2,2,2], cost = [4,2,8,1,3]
<strong>Output:</strong> 0
<strong>Explanation:</strong> All the elements are already equal, so no operations are needed.
</pre>

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

<ul>
	<li><code>n == nums.length == cost.length</code></li>
	<li><code>1 &lt;= n &lt;= 10<sup>5</sup></code></li>
	<li><code>1 &lt;= nums[i], cost[i] &lt;= 10<sup>6</sup></code></li>
	<li>Test cases are generated in a way that the output doesn&#39;t exceed&nbsp;2<sup>53</sup>-1</li>
</ul>

def min_cost(nums, costs):
    n = len(nums)
    comb = [(nums[i], costs[i]) for i in range(n)]
    comb = sorted(comb, key=lambda x: x[0])
    
    arr = [x[0] for x in comb]
    cost = [y[1] for y in comb]

    min_el = min(nums)
    max_el = max(nums)
    
    X = []
    Y = []
    for el, c in zip(arr, cost):
        X.append((el - min_el) * c)
        Y.append((max_el - el) * c)
        
    # prefix cost
    def prefix_arr(a):
        n = len(a)
        pref = [0] * n
        pref[0] = a[0]
        
        for i in range(1, n):
            pref[i] = pref[i - 1] + a[i]
        return pref
    
    def rs(cs, a, b):
        if a > b:
            return 0
        if a == 0:
            return cs[b]
        return cs[b] - cs[a - 1]
    
    cpref = prefix_arr(cost)
    xpref = prefix_arr(X)
    ypref = prefix_arr(Y)

    mini = None
    for i in range(n):
        el = arr[i]
        cright = rs(xpref, i + 1, n - 1) - rs(cpref, i + 1, n - 1) * (el - min_el)
        cleft = rs(ypref, 0, i - 1) - rs(cpref, 0, i - 1) * (max_el - el)
        # combine both
        c = cleft + cright
        mini = min(mini, c) if mini is not None else c

    return mini

class Solution:
    def minCost(self, nums: List[int], cost: List[int]) -> int:
        return min_cost(nums, cost)
387