一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
二、解题报告
1、思路分析
力扣有个差不多的但是简单点的题目:2972. 统计移除递增子数组的数目 II
由于f(l, r)是值域pair,所以我们考虑将原数组a转换为值域数组nums
nums[i] = (v, l, r),代表值v,最早出现下标为l,最晚出现下标为r
任意一个原数组删除部分值域后剩下的的递增数组都可以对应到nums中的一个“递增数组”:
(“递增数组”代表力扣那道题的移除子数组后剩下的元素形成递增数组的子数组)
nums(i, j),任意 nums[k], i <= k < j, 有nums[k].r < nums[k + 1].l
也就是说,原操作得到的递增数组 和 nums 中的“递增数组”是一个1-1映射关系
那么我们求nums上的“递增数组”的个数即可
这就是比较典的问题了
考虑只保留前缀,那么我们预处理出递增前缀为nums[0, i]
那么贡献就是
nums[i + 1][0] * (x - nums[n - 1][0] + 1),即至少要删除掉[nums[i].v + 1, x]的数
然后考虑一般情况:
双指针i, j
i初始就是最长递增前缀的下标,j初始就是n - 1(n是nums的长度)
由于二者具有单调性: j 靠左移动,和其匹配的i的边界也得左移
所以我们维护i, j,对于一个j,其贡献就是 (nums[j].v - nums[j - 1].v) * nums[i].v,即至少要删除区间下界不超过nums[i].v + 1, 上界不小于nums[j - 1].v
2、复杂度
时间复杂度: O(N)空间复杂度:O(N)
3、代码详解
import sys
input = lambda: sys.stdin.readline().strip()
MII = lambda: map(int, input().split())
LMI = lambda: list(map(int, input().split()))
LI = lambda: list(input())
II = lambda: int(input())
fmax = lambda x, y: x if x > y else y
fmin = lambda x, y: x if x < y else y
P = 10**9 + 7
def solve():
n, x = MII()
pos = [[-1, -1] for _ in range(x + 1)]
a = LMI()
for i, v in enumerate(a):
if pos[v][0] == -1:
pos[v][0] = i
pos[v][1] = i
nums = []
for v, (a, b) in enumerate(pos):
if ~a:
nums.append([v, a, b])
n = len(nums)
i = 0
while i + 1 < n and nums[i][2] < nums[i + 1][1]:
i += 1
if i == n - 1:
print((x + 1) * x // 2)
return
res = nums[i + 1][0] * (x - nums[n - 1][0] + 1)
j = n - 1
while j == n - 1 or nums[j][2] < nums[j + 1][1]:
while ~i and nums[i][2] >= nums[j][1]:
i -= 1
res += nums[i + 1][0] * (nums[j][0] - nums[j - 1][0])
j -= 1
print(res)
if __name__ == "__main__":
T = 1
# T = II()
for _ in range(T):
solve()