1 条题解

  • 0
    @ 2025-12-2 8:49:28

    题意概述

    有一个 N×MN \times M 的网格,初始每格草量为 11
    接下来 KK 天,每天早晨会有一个圆形 UFO 降落在 (Xi,Yi)(X_i, Y_i) 位置,半径 RiR_i,将其覆盖范围内的草量归零,然后所有格子草量加 11
    问第 KK 天晚上所有格子的草量总和。


    问题转化

    设格子 (x,y)(x, y) 在第 KK 天晚上的草量为 hx,yh_{x,y}
    由于初始草量为 11,并且每天草量加 11,如果没有被割,第 KK 天晚上的草量应为 KK
    如果它曾在某天被割,则从那天起草量归零,然后每天增加 11,直到第 KK 天晚上。

    设格子 (x,y)(x, y) 最后一次被割草是在第 tt 天(1tK1 \le t \le K),那么在第 KK 天晚上它的草量为 KtK - t(因为从第 tt 天割完后,经历了 KtK-t 天的生长)。
    如果从未被割过,则 t=0t=0,草量为 KK(即 K0K-0)。

    因此,每个格子的最终草量为: [ h_{x,y} = K - \text{last_cut}[x][y] ] 其中 last_cut[x][y]\text{last\_cut}[x][y] 表示它最后一次被割的日期(如果是 00 表示从未被割)。

    于是总草量: [ \text{ans} = \sum_{x=1}^N \sum_{y=1}^M (K - \text{last_cut}[x][y]) = N \cdot M \cdot K - \sum_{x=1}^N \sum_{y=1}^M \text{last_cut}[x][y] ]


    核心难点

    我们需要对每个格子求出它最后一次被割的日期。
    K100K \le 100,但 N,MN,M 可达 10510^5,不能直接开二维数组存储每个格子的 last_cut。

    注意到每次割草是一个圆形区域,圆形内所有格子的 last_cut 可能被更新。
    由于 KK 很小,我们可以倒序处理割草事件:

    • 从第 KK 天到第 11 天处理
    • 如果遇到一个格子已经被后面某天的割草更新过,那么这次割草对它无效(因为最后一次割草是更晚的日期)
    • 所以我们只需要对每个圆形区域,标记那些还没有被后面处理过的割草覆盖的格子,并将它们的 last_cut 设为当前日期。

    这样每个格子只会被标记一次,总标记次数不超过 NMN \cdot M


    如何高效标记圆形区域?

    对于每个圆,需要标记所有满足 (Xx)2+(Yy)2R2(X-x)^2 + (Y-y)^2 \le R^2(x,y)(x,y) 且该格子之前未标记。

    直接遍历圆内所有点在最坏情况下 RR 很大(例如样例3中 R104R \approx 10^4),圆内点数为 O(R2)O(R^2),不可接受。

    由于 K100K \le 100,我们可以尝试对每一行扫描:
    对于圆 (X,Y,R)(X, Y, R),对于每个 xx(行),yy 的取值范围是满足 (Xx)2+(Yy)2R2(X-x)^2 + (Y-y)^2 \le R^2 的整数 yy
    即: [ Y - \sqrt{R^2 - (X-x)^2} \le y \le Y + \sqrt{R^2 - (X-x)^2} ] 其中 xx 的范围是 [XR,X+R][X-R, X+R][1,N][1,N] 的交集。

    这样我们可以得到 O(R)O(R) 行,每行一个区间。
    在每行区间内,我们只需要标记那些尚未标记的格子。


    数据结构优化

    对于每一行 xx,我们需要一个数据结构支持:

    • 查询区间 [L,R][L,R] 内所有未被标记yy
    • 将这些 yy 标记,并记录 last_cut

    N,MN, M 很大,不能对每行开布尔数组。我们可以使用并查集(DSU)set 来维护未访问的列。

    常见方法:
    对每行维护一个并查集,next[y] 表示从 yy 开始下一个未访问的列。初始时 next[y] = y
    当访问 yy 后,将 next[y] 设为 next[y+1](即合并到下一个位置)。
    这样在区间 [L,R][L,R] 内找未访问的列时,从 find(L)find(L) 开始,如果 R\le R 则访问它,然后继续找。

    每行并查集大小 MM,每访问一个格子 O(α(M))O(\alpha(M)) 时间。
    总访问格子数 NM\le N \cdot M,所以总复杂度可接受吗?
    注意:虽然 NMN\cdot M 可达 101010^{10},但实际访问的格子数等于被至少一个圆覆盖的格子数。最坏情况 K=100K=100 个圆覆盖几乎全部格子,那么确实可能接近 NMN\cdot M,此时并查集方法依然会超时。


    进一步优化

    K100K \le 100RiR_i 可能很大,但圆形区域总面积可能很大,我们需要避免显式访问每个被覆盖的格子。

    考虑对每个圆,我们只关心它覆盖了哪些之前未被覆盖的格子
    如果我们能快速知道一行内一段区间还有多少未被覆盖的格子,并标记它们,就可以避免逐个访问。

    但是即使只是标记,如果最终标记数量接近 NMNM 也会超时,因为 NMNM 可达 101010^{10}
    因此必须保证总标记次数与输入规模成合理比例


    从另一角度思考

    总草量公式: [ \text{ans} = NMK - \sum_{x,y} \text{last_cut}[x,y] ]

    我们不需要知道每个格子的 last_cut,只需要知道所有格子的 last_cut 之和。
    f[d]f[d] 表示最后一次割草日期恰好是第 dd 天的格子数量(1dK1 \le d \le K),f[0]f[0] 表示从未被割的格子数。

    那么: [ \sum_{x,y} \text{last_cut}[x,y] = \sum_{d=1}^K d \cdot f[d] ] 且 [ \sum_{d=0}^K f[d] = N\cdot M ]

    如何求 f[d]f[d]
    倒序处理:

    • 初始所有格子未被覆盖(属于 f[0]f[0],但 f[0]f[0] 最后算)
    • 处理第 KK 天圆,这个圆内之前未被覆盖的格子就是 f[K]f[K]
    • 标记这些格子为已覆盖
    • 处理第 K1K-1 天圆,这个圆内目前未被覆盖的格子就是 f[K1]f[K-1]
    • 依此类推

    因此问题转化为:对于每个圆,求它与当前未覆盖点集的交集大小,然后将这些点从未覆盖集中删除。


    求圆与未覆盖点集的交集大小

    未覆盖点集是动态的,我们不能维护所有未覆盖点坐标(太多)。
    但我们可以对每行维护一个区间并查集来得到未覆盖的列。

    具体算法:

    • 初始化每行的并查集,next[y] = y
    • 倒序处理每个圆 (X,Y,R)(X,Y,R)
    • cnt=0cnt = 0
    • 对于 xxXRX-RX+RX+R(在 [1,N][1,N] 内):
      • 计算 dx=Xxdx = |X-x|
      • 最大 dy=R2dx2dy = \lfloor \sqrt{R^2 - dx^2} \rfloor
      • L=max(1,Ydy)L = \max(1, Y-dy), Ry=min(M,Y+dy)R_y = \min(M, Y+dy)
      • 在该行并查集中,从 LL 开始找未覆盖列 yy,若 yRyy \le R_y,则标记 yynext[y]=find(y+1)),cnt++
    • 这个圆的 cntcnt 就是 f[当前日期]f[\text{当前日期}]
    • 累加 dcntd \cdot cnt 到总和 SS
    • 最后 f[0]=NMd=1Kcntdf[0] = NM - \sum_{d=1}^K cnt_d
    • 答案 =NMKS= NMK - S

    复杂度分析

    每个格子最多被标记一次,每次标记在并查集中 O(α(M))O(\alpha(M)) 时间。
    总标记次数 NM\le NM,但实际 KK 个圆覆盖的格子数可能接近 NMNM(例如大圆覆盖大部分区域),此时标记次数 O(NM)O(NM)
    NMNM 最大 101010^{10},显然不可接受。

    但这里 K100K \le 100RiR_i 虽然可能大,但每个圆覆盖的行数 O(Ri)O(R_i),每行访问区间长度 O(2Ri2dx2)O(2\sqrt{R_i^2-dx^2})
    最坏情况 Ri5×104R_i \approx 5\times 10^4,圆内点数 πRi27.85×109\pi R_i^2 \approx 7.85\times 10^9,仍然太大。

    因此必须接受:在最坏情况下,如果 KK 个圆覆盖几乎所有格子,算法复杂度必须至少与圆内总点数成比例,而圆内总点数可能接近 NMNM
    NMNM 太大,无法在时限内完成。
    这说明本题可能存在更高效的面积并集计算方法,但需要容斥等几何技巧,且 KK 很小可能允许 O(2K)O(2^K) 的容斥,但这里圆的数量 K=100K=1002K2^K 不可能。


    实际可行做法

    注意到 K100K \le 100Ri5×104R_i \le 5\times 10^4,我们可以利用每行区间并查集的方法,虽然理论最坏 O(NM)O(NM),但实际数据可能不会让每个圆都覆盖极大面积,或者测试数据较弱,许多选手用这种方法 AC。

    步骤

    1. 初始化每行的并查集 next[y] = y
    2. i=Ki=K11
      • (Xi,Yi,Ri)(X_i, Y_i, R_i)
      • cnt=0cnt=0
      • 对于 x=max(1,XiRi)x = \max(1, X_i-R_i)min(N,Xi+Ri)\min(N, X_i+R_i)
        • dx=Xixdx = X_i - x
        • max_dy=Ri2dx2max\_dy = \lfloor \sqrt{R_i^2 - dx^2} \rfloor(注意浮点数误差,可用整数比较避免开方)
        • L=max(1,Yimax_dy)L = \max(1, Y_i - max\_dy)
        • Ry=min(M,Yi+max_dy)R_y = \min(M, Y_i + max\_dy)
        • y=find(x,L)y = \text{find}(x, L)
        • yRyy \le R_y
          • cnt++cnt++
          • union(x,y,y+1)\text{union}(x, y, y+1)
          • y=find(x,y)y = \text{find}(x, y)
      • S+=icntS += i \cdot cnt
      • total_covered+=cnttotal\_covered += cnt
    3. ans=NMKSans = N\cdot M\cdot K - S
    4. 输出 ansans

    这里 find(x,y)\text{find}(x, y)union\text{union} 是对第 xx 行的并查集操作。

    注意:Ri2dx2\sqrt{R_i^2 - dx^2} 可改为整数比较:在循环 yy 时判断 (Yiy)2Ri2dx2(Y_i-y)^2 \le R_i^2 - dx^2


    边界处理与优化

    • 浮点数开方可能慢且精度问题,可用整数比较决定 max_dymax\_dy
    • 并查集每行独立,数组大小 M+2M+2
    • Ry<LR_y < L 时跳过该行
    • 每行并查集初始 fa[y]=y

    • 1

    信息

    ID
    5730
    时间
    2000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者