給定 n 個長方體(n ≤ 1000),每個由 (x₁,y₁,z₁) 到 (x₂,y₂,z₂) 確定。若某區域被≥ 3 個長方體覆蓋,則該區域確定有寶藏。求寶藏區域的最小體積(即覆蓋次數 ≥ 3 的區域體積和)。座標範圍:|x|,|y| ≤ 10⁶,|z| ≤ 500。
每個節點維護:
once:覆蓋 ≥ 1 次的總長度twice:覆蓋 ≥ 2 次的總長度more:覆蓋 ≥ 3 次的總長度(即我們要的)三者包含關係:more ⊆ twice ⊆ once。節點另存 cnt 為懶標記(完全覆蓋次數)。
void push_up(int rt, int l, int r) {
// once:覆蓋 ≥ 1
if (cnt[rt]) once[rt] = X[r+1] - X[l];
else if (l == r) once[rt] = 0;
else once[rt] = once[rt<<1] + once[rt<<1|1];
// twice:覆蓋 ≥ 2
if (cnt[rt] > 1) twice[rt] = X[r+1] - X[l];
else if (l == r) twice[rt] = 0;
else if (cnt[rt] == 1) twice[rt] = once[rt<<1] + once[rt<<1|1];
else twice[rt] = twice[rt<<1] + twice[rt<<1|1];
// more:覆蓋 ≥ 3
if (cnt[rt] > 2) more[rt] = X[r+1] - X[l];
else if (l == r) more[rt] = 0;
else if (cnt[rt] == 2) more[rt] = once[rt<<1] + once[rt<<1|1];
else if (cnt[rt] == 1) more[rt] = twice[rt<<1] + twice[rt<<1|1];
else more[rt] = more[rt<<1] + more[rt<<1|1];
}
關鍵洞察:利用包含關係避免重複加減,cnt==2 時 more = 子節點 once 之和(因 once 已含 twice),cnt==1 時 more = 子節點 twice 之和。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define ll long long
const int maxn = 2222;
using namespace std;
int cnt[maxn<<2], X[maxn], Z[maxn];
int once[maxn<<2], twice[maxn<<2], more[maxn<<2];
struct Cube {
struct Pt { int x, y, z; void read() { scanf("%d%d%d", &x, &y, &z); } } a, b;
} cube[maxn];
struct Seg {
int l, r, h, s; // [l,r) 高度 h, +1 入 / -1 出
bool operator<(const Seg& o) const { return h < o.h; }
} seg[maxn<<2];
void push_up(int rt, int l, int r) {
if (cnt[rt]) once[rt] = X[r+1] - X[l];
else if (l == r) once[rt] = 0;
else once[rt] = once[rt<<1] + once[rt<<1|1];
if (cnt[rt] > 1) twice[rt] = X[r+1] - X[l];
else if (l == r) twice[rt] = 0;
else if (cnt[rt] == 1) twice[rt] = once[rt<<1] + once[rt<<1|1];
else twice[rt] = twice[rt<<1] + twice[rt<<1|1];
if (cnt[rt] > 2) more[rt] = X[r+1] - X[l];
else if (l == r) more[rt] = 0;
else if (cnt[rt] == 2) more[rt] = once[rt<<1] + once[rt<<1|1];
else if (cnt[rt] == 1) more[rt] = twice[rt<<1] + twice[rt<<1|1];
else more[rt] = more[rt<<1] + more[rt<<1|1];
}
void update(int L, int R, int v, int l, int r, int rt) {
if (L <= l && r <= R) {
cnt[rt] += v;
push_up(rt, l, r);
return;
}
int m = (l + r) >> 1;
if (L <= m) update(L, R, v, lson);
if (m < R) update(L, R, v, rson);
push_up(rt, l, r);
}
void solve(int m) {
sort(X, X + 2*m);
sort(Z, Z + 2*m);
int kx = unique(X, X + 2*m) - X;
int kz = unique(Z, Z + 2*m) - Z;
ll ans = 0;
for (int iz = 0; iz < kz - 1; ++iz) {
int zLow = Z[iz], zHigh = Z[iz+1];
int tot = 0;
for (int i = 0; i < m; ++i) {
if (cube[i].a.z <= zLow && cube[i].b.z > zLow) {
int x1 = cube[i].a.x, x2 = cube[i].b.x;
int y1 = cube[i].a.y, y2 = cube[i].b.y;
seg[tot++] = {x1, x2, y1, 1};
seg[tot++] = {x1, x2, y2, -1};
}
}
sort(seg, seg + tot);
memset(cnt, 0, sizeof(cnt));
memset(once, 0, sizeof(once));
memset(twice, 0, sizeof(twice));
memset(more, 0, sizeof(more));
for (int i = 0; i < tot - 1; ++i) {
int l = lower_bound(X, X + kx, seg[i].l) - X;
int r = lower_bound(X, X + kx, seg[i].r) - X - 1;
if (l <= r) update(l, r, seg[i].s, 0, kx-1, 1);
ans += (ll)(zHigh - zLow) * more[1] * (seg[i+1].h - seg[i].h);
}
}
printf("%lld\n", ans);
}
int main() {
int T, n, cas = 1;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
cube[i].a.read(); X[2*i] = cube[i].a.x; Z[2*i] = cube[i].a.z;
cube[i].b.read(); X[2*i+1] = cube[i].b.x; Z[2*i+1] = cube[i].b.z;
}
printf("Case %d: ", cas++);
solve(n);
}
return 0;
}
輸入:
2
1
0 0 0 5 6 4
3
0 0 0 5 5 5
3 3 3 9 10 11
3 3 3 13 20 45
輸出:
Case 1: 0
Case 2: 8
與 Sample Output 一致。