P5490.扫描线(python)

这个洛谷怎么对于python不太友好呢,没几次能全过的

本题使用扫描线的模板,首先把所有x坐标排序去重,放进列表X中。把所有横线lines排序。这样把所有矩阵都分成了块。对于每一块,高=lines[i+1]-lines[i],宽就等于在这一块中,每个矩阵的并。

比如说图中,纵坐标在3-5之间,那么高度就是2,其中有两块矩阵并起来,计算并起来的宽度=80-10=70,高×宽就是这一块的面积。

所有块的面积之和就是ans。

class node:
    def __init__(self, l = None, r = None, cnt = 0, len = 0):
        self.l = l
        self.r = r
        self.cnt = cnt
        self.len = len

def build(idx, l, r):
    tr[idx] = node(l, r, 0, 0)
    if l == r: return
    mid = l + r >> 1
    build(2*idx, l, mid)
    build(2*idx+1, mid+1, r)

def dich(num):
    l, r = 0, length-1
    while l < r:
        mid = l + r >> 1
        if X[mid] < num:
            l = mid + 1
        else:
            r = mid
    return l


def pushup(idx):
    if tr[idx].cnt:
        tr[idx].len = X[tr[idx].r+1] - X[tr[idx].l]
    else:
        try:
            tr[idx].len = tr[2*idx].len + tr[2*idx+1].len
        except:
            tr[idx].len = 0


def modify(idx, l, r, tag):
    if tr[idx].l > r or tr[idx].r < l: return
    elif l <= tr[idx].l and tr[idx].r <= r:
        tr[idx].cnt += tag
        pushup(idx)
        return
    modify(2*idx, l, r, tag)
    modify(2*idx+1, l, r, tag)
    pushup(idx)

n = int(input())
X = [-float('inf')]
lines = []
# tr中存放所有节点的,l, r, cnt覆盖次数, len覆盖长度
tr = [node()]*(8*n)
ans = 0
for i in range(n):
    x1, y1, x2, y2 = map(int, input().split())
    X.append(x1)
    X.append(x2)
    lines.append([y1, x1, x2, 1])
    lines.append([y2, x1, x2, -1])
X = sorted(list(set(X)))
length = len(X)
lines.sort()
build(1, 1, length-2)
# 扫描开始
for i in range(len(lines)-1):
    y, x1, x2, tag = lines[i]
    l = dich(x1)
    r = dich(x2)-1
    modify(1, l, r, tag)
    ans += tr[1].len*(lines[i+1][0] - lines[i][0])
print(ans)

其中需要注意的是,把X排完序,想要从坐标1开始计算,那在X最前面加一个-inf,保证排序从1开始。

又发现X是离散的,所以需要离散化

这样线段树的就以下标为值,例如根节点就是1-5。那么有人就会问为什么不是1-6呢?

如果是1-6,它的左节点就是1-4,右节点就是5-6,我们发现从4到5,实际上差了20,显然不可行。

我们让右左标偏移。

这样右边的5就代表90,左边的1还是代表10。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值