一、 题目
1. 题目描述
我们给出了一个(轴对齐的)二维矩形列表 rectangles
。 对于 rectangle[i] = [x1, y1, x2, y2]
,其中(x1,y1)是矩形 i
左下角的坐标, (xi1, yi1)
是该矩形 左下角 的坐标, (xi2, yi2)
是该矩形 右上角 的坐标。
计算平面中所有 rectangles
所覆盖的 总面积 。任何被两个或多个矩形覆盖的区域应只计算 一次 。
返回 总面积 。因为答案可能太大,返回 109 + 7
的 模 。
示例 1:
输入:rectangles = [[0,0,2,2],[1,0,2,3],[1,0,3,1]]
输出:6
解释:如图所示,三个矩形覆盖了总面积为6的区域。
从(1,1)到(2,2),绿色矩形和红色矩形重叠。
从(1,0)到(2,3),三个矩形都重叠。
示例 2:
输入:rectangles = [[0,0,1000000000,1000000000]]
输出:49
解释:答案是 1018 对 (109 + 7) 取模的结果, 即 49 。
提示:
1 <= rectangles.length <= 200
rectanges[i].length = 4
0 <= xi1, yi1, xi2, yi2 <= 109
- 矩形叠加覆盖后的总面积不会超越
2^63 - 1
,这意味着可以用一个 64 位有符号整数来保存面积结果。
Related Topics
- 线段树
- 数组
- 有序集合
- 扫描线
- 👍 102
- 👎 0
二、 解题报告
1. 思路分析
这道题是线段树处理矩形面积并模板题。
按x顺序遍历每一条竖边,累计入度,矩形的左边是入边,右边是出边。
入度只会是正数或0。
用线段树维护当前y上覆盖距离,乘上x间距即可
当然不用线段树也有,有很多种解法。
2. 复杂度分析
最坏时间复杂度O(nlog2n)
3. 代码实现
线段树
。
""" 题解
线段树处理矩形面积并模板题,对y要离散化
一定要注意语法!!
相当于通过线段树的归并,永远更新根节点,用根节点计算当前覆盖的长度。
"""
class IntervalTreeNode:
def __init__(self, len, cover):
self.len = len
self.cover = cover
class IntervalTree:
def __init__(self, size, ys=None):
self.size = size
# self.interval_tree = [IntervalTreeNode(0,0)]*(size*4) ## 这个地方wa了很久,不能这么写,一直更新一个实例
self.interval_tree = [IntervalTreeNode(0, 0) for _ in range(size * 4)]
self.ys = ys
def update_from_son(self, p, l, r):
interval_tree = self.interval_tree
pn = interval_tree[p]
if pn.cover > 0:
pn.len = self.ys[r - 1] - self.ys[l - 1]
else:
if l + 1 == r:
pn.len = 0
else:
pn.len = interval_tree[p * 2].len + interval_tree[p * 2 + 1].len
def insert(self, p, l, r, x, y, cover):
interval_tree = self.interval_tree
if x <= l and r <= y:
interval_tree[p].cover += cover
self.update_from_son(p, l, r)
return
mid = (l + r) // 2
if x < mid:
self.insert(p * 2, l, mid, x, y, cover)
if y > mid:
self.insert(p * 2 + 1, mid, r, x, y, cover)
self.update_from_son(p, l, r)
class LineY:
def __init__(self, x, y1, y2, cover):
self.x = x
self.y1 = y1
self.y2 = y2
self.cover = cover
class Solution:
def rectangleArea(self, rectangles: List[List[int]]) -> int:
lines = [] # 所有竖线线段
ys = set() # 离散化
for x1, y1, x2, y2 in rectangles:
lines.append(LineY(x1, y1, y2, 1))
lines.append(LineY(x2, y1, y2, -1))
ys.add(y1)
ys.add(y2)
lines.sort(key=lambda x: x.x)
line_count = len(lines)
ys = list(ys)
ys.sort()
interval_tree = IntervalTree(line_count, ys=ys)
ans = 0
mod = int(1e9 + 7)
for i in range(0, line_count):
line = lines[i]
# print(line.x,line.y1,line.y2,line.cover)
y1 = bisect_left(ys, line.y1)
y2 = bisect_left(ys, line.y2)
# if y1==y2:
# continue
if i > 0:
ans += (line.x - lines[i - 1].x) * interval_tree.interval_tree[1].len
ans %= mod
interval_tree.insert(1, 1, len(ys), y1 + 1, y2 + 1, line.cover)
return ans
# 注意以下是n方算法,n大了过不了,这种还是适合辛普森的题
class Line:
def __init__(self,l,r):
self.l,self.r=l,r
def __lt__(self,other): # 线段按起点排序
return self.l<other.l
class Rect:
def __init__(self,x1,y1,x2,y2):
self.x1,self.y1,self.x2,self.y2=x1,y1,x2,y2
def __lt__(self,other): # 方块按左边界排序
return self.x1<other.x1
class Solution2:
def rectangleArea(self, rectangles: List[List[int]]) -> int:
size = len(rectangles)
rects = [] # 所有方块
xs = set() # 统计
for x1, y1, x2, y2 in rectangles:
rects.append(Rect( x1, y1, x2, y2 ))
xs.add(x1)
xs.add(x2)
xs = sorted(list(xs))
def sort_rect_by_left(rects):
a = ([(rect.x1,rect) for rect in rects])
a.sort()
return [rect for x,rect in a]
# rects = sort_rect_by_left(rects)
rects.sort()
def sort_line_by_l(lines):
a = sorted([(line.l,line) for line in lines])
return [line for _ ,line in a]
def get_f(x):
lines = []
for rect in rects:
x1,y1,x2,y2 = rect.x1,rect.y1,rect.x2,rect.y2
if x<=x1: # 这个方块在线右边,后边不用算了
break
if x2<=x: # 这个方块在线的左边,它自己不用算
continue
lines.append(Line(y1,y2))
# lines = sort_line_by_l(lines)
lines.sort()
line_size = len(lines)
f = 0
i = 0
while i < line_size:
r = lines[i].r
j = i + 1
while j < line_size:
if lines[j].l > r:
break
r = max(r, lines[j].r)
j += 1
f += r - lines[i].l
i = j
return f
ans = 0
mod = 10**9 + 7
for i in range(1, len(xs)):
width = xs[i] - xs[i-1]
x = xs[i-1]+ width/2
ans += width * get_f(x)
ans %= mod
return ans
三、 本题小结
- 线段树计算矩形面积并