P5567 [SDOI2008] 立方体覆盖-题解

· · 题解

题意

N 个正方体,求他们的体积并。

数据范围:N\le100,给出的是正方体中心的坐标以及半径 (x,y,z,r),有 -1000\le x,y,z\le1000,r\le200

题解

分离出正方体的左右平面,先按 x 的顺序从左到右扫,问题变成维护矩阵加、求全局有多少个数大于 0。

看到数据范围这么小直接暴力,对每一行正常维护一棵线段树即可。

## 代码 ```cpp #include <bits/stdc++.h> using namespace std; #define rep(i,j,k) for(int i=j;i<=k;i++) const int N = 2010; struct XX { int x, Ly, Lz, Ry, Rz, v; } a[210]; int n, m = 2001, tot, w, b[N][N << 2], t[N][N << 2]; long long as; #define mid ((l + r) >> 1) #define lc (u << 1) #define rc ((u << 1) | 1) void Up(int u, int l, int r) { if (t[w][u]) b[w][u] = r - l + 1; else if (l != r) b[w][u] = b[w][lc] + b[w][rc]; else b[w][u] = 0; } void Upd(int u, int l, int r, int x, int y, int v) { if (y < l || r < x) return; if (x <= l && r <= y) return t[w][u] += v, Up(u, l, r); Upd(lc, l, mid, x, y, v), Upd(rc, mid + 1, r, x, y, v), Up(u, l, r); } void Upd(int Lx, int Ly, int Rx, int Ry, int v) { for (w = Lx; w <= Rx; w++) Upd(1, 1, m, Ly, Ry, v); } int main() { scanf("%d", &n); rep(i, 1, n) { int x, y, z, r; scanf("%d%d%d%d", &x, &y, &z, &r), x += 1001, y += 1001, z += 1001; a[++tot] = {x - r, y - r + 1, z - r + 1, y + r, z + r, 1}; a[++tot] = {x + r, y - r + 1, z - r + 1, y + r, z + r, -1}; } sort(a + 1, a + tot + 1, [](XX u, XX v) { return u.x < v.x; }); Upd(a[1].Ly, a[1].Lz, a[1].Ry, a[1].Rz, a[1].v); rep(i, 2, tot) { long long s = 0; rep(j, 1, m) s += b[j][1]; as += 1ll * (a[i].x - a[i - 1].x) * s; Upd(a[i].Ly, a[i].Lz, a[i].Ry, a[i].Rz, a[i].v); } printf("%lld\n", as); } ``` ### 小引申 如果变成一般性的长方体覆盖,怎么做? 把 $x,y,z$ 全部离散化,对每一维做一次扫描线是 $\mathcal O(n^2\log n)$ 的。 更直接的想法是 $\mathcal O(n\log^3n)$: 问题变成需要支持 矩阵加 和 对整个平面查询有多少个数大于 0(要先离散化): 树套树,但是外层线段树每个节点都对应一棵线段树,设外层线段树进行了一次区间加,涉及到了 $\log n$ 个节点; 这些点和他们的所有祖先受到了影响,由于深度 $\log$ 层,收到影响的最多有 $\log^2n$ 个节点; 对所有受到影响的这些点代表的线段树进行 update,$\mathcal O(n\log^3n)$,十分无脑;空间 $\mathcal O(n^2)$。 我尝试对这个方法进行标记永久化,但是没想出来…… 或者分块什么的?