1712. 将数组分成三个子数组的方案数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution:
def waysToSplit(self, nums: List[int]) -> int:
# 用先 切一刀;再切一刀的思路作。后一刀 下界 和 上界的确定用二分法。
n = len(nums)
sum_ = [0] * n # 作一个前缀和数组
sum_[0] = nums[0]
for i in range(1,n):
sum_[i] = sum_[i-1]+nums[i]
# 第一刀 小于 总和的 1/3
ans = 0
i = 0
while i<n and sum_[i]<=sum_[n-1]/3:
# 在第一刀的基础上,确定第二刀的下界;sum(left)==sum(mid)
# idx = bisect.bisect_left(sum_[i+1:],sum_[i]*2) # 这样的简写形式会超时
# left = idx + i + 1
left = self.lower_range(i+1,n-1,sum_,sum_[i]*2)
# 在第一刀的基础上,确定第二刀的上界;sum(mid)==[sum(mid)+sum(right)]/2
# idx = bisect.bisect_right(sum_[i+1:],sum_[i]+(sum_[n-1]-sum_[i])/2)-1
# right = idx + i + 1
right = self.upper_range(i+1,n-1,sum_,sum_[i]+(sum_[n-1]-sum_[i])/2)
right = right if right<n-1 else right-1
# 第一刀和第二刀是 一条龙操作的,所以只需要胖段第二刀的所有可能性 即可。
print(len(nums),right)
if (left<=right):
ans += right - left + 1
i += 1
return ans%(10**9 + 7)

def lower_range(self,i,j,nums,target):
left = i
right = j
while left<=right:
mid = (left+right)>>1
if nums[mid]>=target:
right = mid-1
else:
left = mid+1
return right+1
def upper_range(self,i,j,nums,target):
left = i
right = j
while left<=right:
mid =(left+right)>>1
if nums[mid]<=target:
left = mid+1
else:
right = mid-1
return left-1