目录

0850:矩形面积 II(2235 分)

力扣第 88 场周赛第 4 题

题目

给你一个轴对齐的二维数组 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

分析

#1

  • 类似 0218,不过遍历坐标 x 时,要维护的不是高度集合,而是区间集合
  • 每一轮根据当前横坐标 x2 、上一轮横坐标 x1,上一轮区间集合覆盖的长度 w,即可计算出 x1 到 x2 的面积
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from sortedcontainers import SortedList
mod = 10**9+7
class Solution:
    def rectangleArea(self, rectangles: List[List[int]]) -> int:
        def cal(A):
            res, end = 0, 0
            for s, e in A:
                res += max(end, e) - max(s, end)
                end = max(end, e)
            return res
        d = defaultdict(list)
        for x1,y1,x2,y2 in rectangles:
            d[x1].append((1,y1,y2))
            d[x2].append((0,y1,y2))
        res = 0
        H = SortedList()
        for x1,x2 in pairwise(sorted(d)):
            for flag,y1,y2 in d[x1]:
                if flag:
                    H.add((y1,y2))
                else:
                    H.remove((y1,y2))
            res += cal(H)*(x2-x1)
            res %= mod
        return res

时间复杂度 O(N^2),15 ms

#2

  • 还可以用线段树维护区间覆盖的长度 w
  • 直接维护覆盖的长度不好处理,一种方法是维护 区间的最小值 t 和 t 对应的长度 sz
    • 假如总区间的最小值 t>0,说明整个区间都被覆盖
    • 否则 t=0,覆盖的长度为总区间长度 s - (t 对应的 sz)

解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Seg:
    def __init__(self,n):
        self.L = n.bit_length()
        self.N = N = 1<<self.L
        self.t = [0]*N*2
        self.f = [0]*N*2
        self.sz = [0]*N*2
        
    def apply(self,o,x):            
        self.t[o] += x
        self.f[o] += x

    def build(self,A):
        for i in range(len(A)-1):
            self.sz[self.N+i] = A[i+1]-A[i]
        for o in range(self.N-1,0,-1):
            self.pull(o)

    def pull(self,o):
        t,sz = self.t,self.sz
        t[o] = min(t[o*2],t[o*2+1])
        sz[o] = sz[o*2]*(t[o]==t[o*2])+sz[o*2+1]*(t[o]==t[o*2+1])

    def push(self,o):
        if self.f[o] != self.f[0]:
            self.apply(o*2,self.f[o])
            self.apply(o*2+1,self.f[o])
            self.f[o] = self.f[0]

    def modify(self,a,b,x):
        a,b = a+self.N-1,b+self.N+1  
        for i in range(self.L,0,-1):  
            self.push(a>>i)  
            self.push(b>>i)  
        while a^b^1:  
            if not a&1: self.apply(a^1,x)  
            if b&1: self.apply(b^1,x)  
            a,b = a>>1,b>>1  
            self.pull(a)  
            self.pull(b)  
        while a>>1:
            a >>= 1
            self.pull(a)
    
mod = 10**9+7
class Solution:
    def rectangleArea(self, rectangles: List[List[int]]) -> int:
        d = defaultdict(list)
        V = sorted({y for _,y1,_,y2 in rectangles for y in [y1,y2]})
        mp = {y:i for i,y in enumerate(V)}
        for x1,y1,x2,y2 in rectangles:
            y1,y2 = mp[y1],mp[y2]-1
            d[x1].append((y1,y2,1))
            d[x2].append((y1,y2,-1))
        res = 0
        seg = Seg(len(V)-1)
        seg.build(V)
        for x1,x2 in pairwise(sorted(d)):
            for y1,y2,flag in d[x1]:
                seg.modify(y1,y2,flag)
            w = V[-1]-V[0]-(seg.sz[1] if seg.t[1]==0 else 0)
            res += w*(x2-x1)
            res %= mod
        return res

19 ms