<p>You are given an array of <strong>positive</strong> integers <code>nums</code> of length <code>n</code>.</p>

<p>We call a pair of <strong>non-negative</strong> integer arrays <code>(arr1, arr2)</code> <strong>monotonic</strong> if:</p>

<ul>
	<li>The lengths of both arrays are <code>n</code>.</li>
	<li><code>arr1</code> is monotonically <strong>non-decreasing</strong>, in other words, <code>arr1[0] &lt;= arr1[1] &lt;= ... &lt;= arr1[n - 1]</code>.</li>
	<li><code>arr2</code> is monotonically <strong>non-increasing</strong>, in other words, <code>arr2[0] &gt;= arr2[1] &gt;= ... &gt;= arr2[n - 1]</code>.</li>
	<li><code>arr1[i] + arr2[i] == nums[i]</code> for all <code>0 &lt;= i &lt;= n - 1</code>.</li>
</ul>

<p>Return the count of <strong>monotonic</strong> pairs.</p>

<p>Since the answer may be very large, return it <strong>modulo</strong> <code>10<sup>9</sup> + 7</code>.</p>

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

<div class="example-block">
<p><strong>Input:</strong> <span class="example-io">nums = [2,3,2]</span></p>

<p><strong>Output:</strong> <span class="example-io">4</span></p>

<p><strong>Explanation:</strong></p>

<p>The good pairs are:</p>

<ol>
	<li><code>([0, 1, 1], [2, 2, 1])</code></li>
	<li><code>([0, 1, 2], [2, 2, 0])</code></li>
	<li><code>([0, 2, 2], [2, 1, 0])</code></li>
	<li><code>([1, 2, 2], [1, 1, 0])</code></li>
</ol>
</div>

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

<div class="example-block">
<p><strong>Input:</strong> <span class="example-io">nums = [5,5,5,5]</span></p>

<p><strong>Output:</strong> <span class="example-io">126</span></p>
</div>

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

<ul>
	<li><code>1 &lt;= n == nums.length &lt;= 2000</code></li>
	<li><code>1 &lt;= nums[i] &lt;= 1000</code></li>
</ul>

class Solution:
    def countOfPairs(self, nums: List[int]) -> int:
        n = len(nums)
        mod = 10**9 + 7
        mx = max(nums)
        
        dp = [[0]*(mx+1) for _ in range(n)]
        for num1 in range(mx+1):
            num2 = nums[0] - num1
            if num2 >= 0:
                dp[0][num1] = 1
            else:
                break
        presum_dp = [[0]*(mx+1) for _ in range(n)]
        presum_dp[0][0] = dp[0][0]
        for num in range(1, mx+1):
            presum_dp[0][num] = presum_dp[0][num-1] + dp[0][num]

        for i in range(1, n):

            for num1 in range(nums[i]+1):
                num2 = nums[i]-num1
                if num2 < 0: break

                upperbound = min(nums[i-1]-num2, num1)
                # for prev_num1 in range(upperbound+1):
                #     dp[i][num1] += dp[i-1][prev_num1]
                #     dp[i][num1] %= mod

                if upperbound >= 0:
                    dp[i][num1] += presum_dp[i-1][upperbound]
                    dp[i][num1] %= mod
            
            presum_dp[i][0] = dp[i][0]
            for num in range(1, mx+1):
                presum_dp[i][num] = presum_dp[i][num-1] + dp[i][num]

        return sum(dp[n-1]) % mod

# # Intuition

# 3250. Find the Count of Monotonic Pairs I 的 follow-up

# 原3250題的解法, 時間上O(n * max(nums[i]]) * max(nums[i]])) ~ O(2000 * 1000 * 1000) 會超時
# 從數據上來看, 時間複雜度上僅能允許`O(n * max(nums[i]))`

# 遍歷`i`跟`num1`感覺是無法避免, 所以很直覺地會想說該如何優化第三層循環那部分:

# ```py
# for i in range(1, n):

#     for num1 in range(nums[i]+1):
#         num2 = nums[i]-num1
#         if num2 < 0: break

#         for prev_num1 in range(min(nums[i-1]-num2, num1)+1):
#             dp[i][num1] += dp[i-1][prev_num1]
#             dp[i][num1] %= mod

# return sum(dp[n-1]) % mod
# ```

# 從上面關係式子`dp[i][num1] += dp[i-1][prev_num1] for prev_num1 in range(min(nums[i-1]-num2, num1)+1)`可看出
# `dp[i][num1]`加上的是一段區間和, 所以感覺我們可以結合**dp + prefix sum**來讓這段O(n)降至O(1)

# 所以我們額外維護個`presum_dp[i][num]`, 其中presum[i][num]代表i-th round的sum(dp[i][0] + ... + dp[i][num])
# 整體概念上會像這樣:

# ```py
# presum_dp = [[0]*(mx+1) for _ in range(n)]
# presum_dp[0][0] = dp[0][0]
# for num in range(1, mx+1):
#     presum_dp[0][num] = presum_dp[0][num-1] + dp[0][num]

# for i in range(1, n):

#     for num1 in range(nums[i]+1):
#         num2 = nums[i]-num1
#         if num2 < 0: break

#         upperbound = min(nums[i-1]-num2, num1)
#         # for prev_num1 in range(upperbound+1):
#         #     dp[i][num1] += dp[i-1][prev_num1]
#         #     dp[i][num1] %= mod

#         if upperbound >= 0:
#             dp[i][num1] += presum_dp[i-1][upperbound]
#             dp[i][num1] %= mod
    
#     presum_dp[i][0] = dp[i][0]
#     for num in range(1, mx+1):
#         presum_dp[i][num] = presum_dp[i][num-1] + dp[i][num]

# return sum(dp[n-1]) % mod
# ```

# 將這段更新改用presum_dp更新:

# ```py
# for prev_num1 in range(upperbound+1):
#     dp[i][num1] += dp[i-1][prev_num1]
#     dp[i][num1] %= mod

# # 上下相對應

# if upperbound >= 0:
#     dp[i][num1] += presum_dp[i-1][upperbound]
#     dp[i][num1] %= mod
# ```

# 然後在更新完dp後, 再以O(max(nums))的時間更新presum_dp

# 所以整體時間複雜度為: O(n * 2 * max(nums))
187692