[WC2002] 奶牛浴场

GitHub跳转原题关系图返回列表

枚举经过产奶点的左边界并收紧上下界,同时单独扫描贴场地左边界的最大空白纵缝。

OJ: luogu

题目 ID: P1578

难度:提高+/省选-

标签:计算几何枚举扫描线python

日期: 2026-07-16 18:25

题意

在矩形场地内放置不含任何产奶点内部的最大轴对齐矩形;点允许位于浴场边界。

思路

最大矩形的左边要么贴场地左边界,要么经过某个产奶点。

贴左边界时,按 x 扫描点并动态维护已进入内部的 y 坐标最大间隙。左边经过点 (left,anchor_y) 时,向右扫描并用遇到的点收紧 lower/upper;同高内部点会把可选纵向区间分成锚点上方或下方两部分。每个新 x 在加入该竖线上的点之前结算面积,因为点可以位于右边界。

Python 知识

  • bisect 维护有序 y 坐标,Counter + heapq 维护当前最大纵缝。
  • set 去掉重复产奶点,它们不会增加约束。
  • 算法为 O(n2)O(n^2),但内层只做整数比较,适合 n5000n\le5000

代码

python
import heapq
import sys
from bisect import bisect_left
from collections import Counter


data = iter(map(int, sys.stdin.buffer.read().split()))
length, width = next(data), next(data)
points = sorted(set((next(data), next(data)) for _ in range(next(data))))

# Rectangles whose left side is the field boundary.
ys = [0, width]
gap_count = Counter({width: 1})
max_gap_heap = [-width]
answer = 0
i = 0
while i < len(points):
    x = points[i][0]
    while max_gap_heap and not gap_count[-max_gap_heap[0]]:
        heapq.heappop(max_gap_heap)
    answer = max(answer, x * -max_gap_heap[0])
    j = i
    while j < len(points) and points[j][0] == x:
        if 0 < x < length:
            y = points[j][1]
            position = bisect_left(ys, y)
            if position == len(ys) or ys[position] != y:
                low, high = ys[position - 1], ys[position]
                gap_count[high - low] -= 1
                for gap in (y - low, high - y):
                    gap_count[gap] += 1
                    heapq.heappush(max_gap_heap, -gap)
                ys.insert(position, y)
        j += 1
    i = j

while max_gap_heap and not gap_count[-max_gap_heap[0]]:
    heapq.heappop(max_gap_heap)
answer = max(answer, length * -max_gap_heap[0])

# Rectangles whose left side passes through a production point.
point_count = len(points)
for left, anchor_y in points:
    lower, upper = 0, width
    split_at_anchor = False
    j = bisect_left(points, (left + 1, -1))
    while j < point_count:
        x, y = points[j]
        if split_at_anchor:
            below, above = anchor_y - lower, upper - anchor_y
            height = below if below > above else above
        else:
            height = upper - lower
        area = (x - left) * height
        if area > answer:
            answer = area
        if y < anchor_y:
            if y > lower:
                lower = y
        elif y > anchor_y:
            if y < upper:
                upper = y
        else:
            split_at_anchor = True
        j += 1
    if split_at_anchor:
        below, above = anchor_y - lower, upper - anchor_y
        height = below if below > above else above
    else:
        height = upper - lower
    area = (length - left) * height
    if area > answer:
        answer = area

print(answer)

复杂度

时间复杂度 O(n2)O(n^2),空间复杂度 O(n)O(n)

总结

最大空矩形必有边界贴场地或穿过障碍点,枚举这条支撑边即可收紧另一维。