<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>

class Solution:          # Binary Search; TC: O(nlogk), SC: O(1); n: len(nums), k: max(nums[i]) - min(nums[i])
    def minCost(self, nums: List[int], cost: List[int]) -> int:
        def getCost(val):
            total = 0
            for i in range(len(nums)):
                total += (abs(val - nums[i]) * cost[i])
            return total
        
        res = getCost(nums[0])          # if all elements are equal
        l, r = min(nums), max(nums)
        while l < r:
            mid = (l + r) // 2
            cost1 = getCost(mid)
            cost2 = getCost(mid + 1)
            res = min(cost1, cost2)
            if cost1 > cost2:           # as cost(mid) > cost(mid+1); cost decreases with increasing mid value
                l = mid + 1
            else:
                r = mid
        return res


# class Solution:          # prefix sum; TC: O(nlogn), SC: O(n); n: len(nums)
#     def minCost(self, nums: List[int], cost: List[int]) -> int:
#         num_and_cost = sorted([num, c] for num, c in zip(nums, cost))     # sort nums by values
#         n = len(cost)
#         prefix_cost = [0] * n
#         prefix_cost[0] = num_and_cost[0][1]
#         for i in range(1, n):              # prefix sum array of cost
#             prefix_cost[i] = num_and_cost[i][1] + prefix_cost[i-1]
#         total_cost = 0
#         for i in range(1, n):              # try every integer nums[i] and make every element equals nums[i]
#             total_cost += num_and_cost[i][1] * (num_and_cost[i][0] - num_and_cost[0][0])
#         ans = total_cost
#         for i in range(1, n):              # The cost difference is made by the change of two parts: 
#             gap = num_and_cost[i][0] - num_and_cost[i-1][0]
#             total_cost += prefix_cost[i-1] * gap                         # 1. prefix sum of costs. 
#             total_cost -= gap * (prefix_cost[n-1] - prefix_cost[i-1])    # 2. suffix sum of costs. 
#             ans = min(ans, total_cost)
#         return ans


# class Solution:              # TC: O(nlogn), SC: O(n)
#     def minCost(self, nums: List[int], cost: List[int]) -> int:
#         arr = sorted(zip(nums, cost))
#         total, cnt = sum(cost), 0
#         for num, c in arr:
#             cnt += c
#             if cnt > total // 2:
#                 target = num
#                 break
#         return sum(c * abs(num - target) for num, c in arr)

589