题目来源
问题描述
小美是美团仓库的管理员,她会根据单据的要求按顺序取出仓库中的货物,每取出一件货物后会把剩余货物重新堆放,使得自己方便查找。已知货物入库的时候是按顺序堆放在一起的。如果小美取出其中一件货物,则会把货物所在的一堆物品以取出的货物为界分成两堆,这样可以保证货物局部的顺序不变。
已知货物最初是按 1~n 的顺序堆放的,每件货物的重量为 w[i] ,小美会根据单据依次不放回的取出货物。请问根据上述操作,小美每取出一件货物之后,重量和最大的一堆货物重量是多少?
反向添加+并查集
图解
按照与取货相反的顺序,不断添加货物。
然后使用并查集,将添加的第 x 个货物与其左 x−1 右 x+1 两边的货物堆进行合并,合并的前提是:
- x−1 和 x+1 的下标合法
- x−1 和 x+1 已经存在(即已经添加过)
然后在添加和合并的过程中维护最大值即可。
首先,这里判断新插入的x是否可以跟左右的合并成一堆,首先判断下标是否合法,然后判断左右的是否出现过(sum[y]),如果出现过,那么假设将数值2指向数值3,那么数值3的和(sum[y])为3+2=5。
mx = max(mx, sum[x])的作用就是判断,如果新出现的是孤立的,即不能合并,那么就输出最大值。这里新出现的是5,那么无法合并。
这里sum[0] = 5, sum[1] = 2, 然后并查集查找合并。
答案是上一轮记下的。
import sys
readline = sys.stdin.readline
def readint():
return int(readline().strip())
def readstr():
return readline().strip()
def readints():
return list(map(int, readline().strip().split()))
n = readint()
w = readints()
o = readints()
mx = 0
fa = list(range(n)) # 并查集的内容
size = [1] * n
sum = [0] * n # 左右堆的和
ans = [0] * n # 最终输出结果
def find_set(x: int) -> int:
if x != fa[x]:
fa[x] = find_set(fa[x])
return fa[x]
def union_sets(x: int, y: int) -> None:
global mx
if 0 <= y < n: # 如果下标y的取值合法
if sum[y]: # 如果下标已经放置
# 找父节点
x, y = find_set(x), find_set(y)
if size[x] > size[y]:
x, y = y, x
fa[x] = y
size[y] += size[x]
sum[y] += sum[x]
mx = max(mx, sum[y])
for i in range(n - 1, -1, -1):
ans[i] = mx
x = o[i] - 1 # 反向取数字下标x
sum[x] = w[x] # 获取下标x的重量w[x]
mx = max(mx, sum[x])
union_sets(x, x - 1) # 合并左边一堆
union_sets(x, x + 1) # 合并右边一堆
print("\n".join(map(str, ans)))