P2698 [USACO12MAR] Flowerpot S
monotonic queue, https://www.luogu.com.cn/problem/P2698

老板需要你帮忙浇花。给出
每滴水均以每秒
如果水滴落在
给出
输入格式
第一行
接下来
输出格式
一行
输入 #1
4 5
6 3
2 4
4 10
12 15输出 #1
2说明/提示
【样例解释】
有
【数据范围】
这是一道经典的双指针(滑动窗口)结合单调队列的题目。
题目分析
- 物理含义:水滴
表示在 坐标处,经过 秒后水滴会落地。 - 核心目标:我们要选定一个区间
(即花盆的宽度为 ),使得落在这个区间内的水滴中,落地的最晚时间与最早时间之差 。 - 数学转化:在按
坐标排序后的水滴序列中,找到一个子序列 ,满足 ,且最小化 。
解题思路
排序:首先将所有水滴按
坐标从小到大排序。 双指针:使用左右两个指针
l和r维护一个窗口。- 右指针
r不断向右移动,增加窗口内的水滴。 - 当窗口内的
时,尝试收缩左指针 l,以减小窗口宽度并更新全局最小值。
- 右指针
单调队列:为了高效地在
时间内获取当前窗口内的最大值和最小值,我们维护两个单调队列: max_q:单调递减队列,队头存储当前窗口的最大值。 min_q:单调递增队列,队头存储当前窗口的最小值。
Python 代码实现
python
import sys
from collections import deque
def solve():
# 使用 fast I/O
input_data = sys.stdin.read().split()
if not input_data:
return
n = int(input_data[0])
d = int(input_data[1])
points = []
idx = 2
for _ in range(n):
x = int(input_data[idx])
y = int(input_data[idx+1])
points.append((x, y))
idx += 2
# 按 x 坐标排序
points.sort()
# 单调队列存储的是 points 的索引
max_q = deque() # y 值单调递减,队头为最大值
min_q = deque() # y 值单调递增,队头为最小值
l = 0
ans = float('inf')
for r in range(n):
# 维护最大值队列
while max_q and points[max_q[-1]][1] <= points[r][1]:
max_q.pop()
max_q.append(r)
# 维护最小值队列
while min_q and points[min_q[-1]][1] >= points[r][1]:
min_q.pop()
min_q.append(r)
# 当满足条件:最大 y - 最小 y >= d 时
while l <= r and points[max_q[0]][1] - points[min_q[0]][1] >= d:
# 更新答案:花盆宽度为当前两个端点的 x 差值
ans = min(ans, points[r][0] - points[l][0])
# 左指针右移,尝试收缩宽度
l += 1
# 移除不在窗口内的索引
if max_q and max_q[0] < l:
max_q.popleft()
if min_q and min_q[0] < l:
min_q.popleft()
if ans == float('inf'):
print("-1")
else:
print(ans)
if __name__ == "__main__":
solve()关键点详解
- 排序的重要性:排序后,随着
r的增加,x[r]是递增的。这使得我们可以利用双指针在的时间内扫描整个数组,而不需要暴力枚举所有区间。 - 队列维护:
max_q队头始终是当前窗口中 最大的那个点的下标。 min_q队头始终是当前窗口中 最小的那个点的下标。
- 时间复杂度:排序耗时
,双指针扫描每个元素进出队列各一次,耗时 。总复杂度 ,完全可以满足 的数据范围。 - 空间复杂度:需要
存储点的数据和队列。
样例运行
对于输入:
4 5
6 3
2 4
4 10
12 15- 排序后:
(2,4), (4,10), (6,3), (12,15) - 当
r=1(点 (4,10)) 时:max=10, min=4,差值。 ans = 4-2 = 2。 - 当
r=2(点 (6,3)) 时:窗口内最大(来自点 (4,10)),最小 (来自点 (6,3)),差值 。尝试收缩 l,ans = min(2, 6-4) = 2。 - 最终输出
2。