跳到主要内容

小美的数组询问

美团 2024-03-09 春招笔试编程题

题目描述​

小美拿到了一个由正整数组成的数组,但其中有一些元素是未知的(用 0 表示)。 现在小美想知道:如果那些未知元素在区间 [l, r] 内随机取值, 数组所有元素之和的最小值和最大值分别是多少?共有 q 次询问。

输入格式​

第一行两个正整数 n、q,代表数组大小和询问次数。 第二行 n 个整数 a[i],其中 a[i] = 0 表示该元素未知。 接下来 q 行,每行两个正整数 l、r,代表一次询问。

输出格式​

输出 q 行,每行两个正整数,代表数组元素之和的最小值和最大值。

示例​

示例 1​

输入:

3 2
1 0 3
1 2
4 4

输出:

5 6
8 8

说明: 只有第二个元素未知。第一次询问范围 [1,2],最小和 1+1+3=5、最大和 1+2+3=6; 第二次询问固定为 4,元素和必然为 1+4+3=8。


解题思路​

第一步:把「未知元素」和「已知元素」分开​

已知元素无论怎么问都不会变,它们的和 base 可以预处理一次; 未知元素有 cnt 个。

第二步:未知元素取边界就是极值​

每个未知元素都在 [l, r] 里取整数,那么要让总和最小就都取 l, 要让总和最大就都取 r:

最小值 = base + cnt * l
最大值 = base + cnt * r

第三步:直接回答每次询问​

每次询问只做一次乘法和加法,O(1)。千万不要每次都重新遍历数组 — 那样是 O(nq),会超时。


完整代码实现​

算法可视化 · 逐帧回放这道题的代码29 帧 · 录制输入
import sys


def solve() -> None:
"""已知和 + 未知个数 × 边界"""
data = sys.stdin.read().split()
if not data:
return
idx = 0
n = int(data[idx]); q = int(data[idx + 1]); idx += 2

base = 0
cnt = 0
for _ in range(n):
v = int(data[idx]); idx += 1
if v == 0:
cnt += 1
else:
base += v

out = []
for _ in range(q):
l = int(data[idx]); r = int(data[idx + 1]); idx += 2
out.append(f"{base + cnt * l} {base + cnt * r}")
sys.stdout.write("\n".join(out) + "\n")


if __name__ == "__main__":
solve()

示例推演​

数组 [1, 0, 3]:base = 1 + 3 = 4,cnt = 1。

询问lr最小值 = 4+1·l最大值 = 4+1·r
11256
24488

复杂度分析​

解法时间复杂度空间复杂度说明
每次询问重新求和O(nq)O(1)超时
预处理 + O(1) 回答O(n + q)O(1)推荐

易错点总结​

1. 把 0 当成真实元素​

0 在这里是「未知」的占位符,不是数组里真的有个 0, 所以它既不能计入 base,也要单独统计个数。

2. 每次询问都重新遍历​

base、cnt 与 l、r 无关,只需预处理一次。

3. 边界可能相等​

当 l == r 时最小值等于最大值,这是合法情况,不用特判。


扩展思考​

  • 若题目要求输出「和的所有可能取值个数」,就要考虑不同取值的组合去重。
  • 「未知元素在区间内取值」这类题的本质是线性函数取端点, 极值一定出现在边界上。

相关题目​

自测​

加载评论中...