题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642
题目要求的是覆盖超过2次的总体积。
线段相交的长度,面相交的面积,物体相交的体积,这三种类型题使用的线段树方法都是差不多的。唯一不同的就是扫描线的扫描方式,这里求相交体积最重要的部分就是怎么扫才比较合适。
网上大部分都是以Z轴,也就是从下到上的顺序扫描的,只要离散化过后,从下到上,从左到右都行,时间都是足够的。
我们以面为单位,也就是扫描面,从左向右扫,但是并不是每一个面都是单独扫,假设我第一步扫最左边第一个面,求出覆盖超过2次的面积,如果有就乘以下一个扫描面的距离差,下一步再扫第二个面,但是并不是单单只扫这个面,还应该把第一个面叠加到第二个面,求这个叠加面覆盖超过2次的面积乘以下一个扫描面的距离差,同理扫描第三个面的时候,需要把第一个和第二个面叠加到第三个面。
下面是代码,代码没有简化,最好根据上面的思路自己写一下。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <set>
#include <queue>
using namespace std;
#define lc (cur<<1)
#define rc ((cur<<1)|1)
const int Maxn = 1000+10;
const int mod = 10007;
const int INF = 0x3f3f3f3f;
typedef long long ll;
struct tree {
int cover, len1, len2, len3;
} seg[Maxn<<4];
struct Edge {
int l ,r, val, xod, zod, ed;
bool operator < (const Edge &a1) const {
return zod < a1.zod;
}
} edge[Maxn<<1];
int L, R, op, tmp[Maxn<<2], X[Maxn<<2], Z[Maxn<<2], Hash[Maxn<<2], tp[Maxn*6];
void pushup(int cur, int l, int r) {
if(seg[cur].cover > 2) {
seg[cur].len3 = Hash[r]-Hash[l-1];
seg[cur].len2 = seg[cur].len1 = 0;
}
else if(seg[cur].cover == 2) {
if(l == r) {
seg[cur].len2 = Hash[r]-Hash[l-1];
seg[cur].len1 = seg[cur].len3 = 0;
} else {
seg[cur].len3 = seg[lc].len1+seg[rc].len1
+seg[lc].len2+seg[rc].len2
+seg[lc].len3+seg[rc].len3;
seg[cur].len2 = Hash[r]-Hash[l-1]-seg[cur].len3;
seg[cur].len1 = 0;
}
}
else if(seg[cur].cover == 1) {
if(l == r) {
seg[cur].len1 = Hash[r]-Hash[l-1];
seg[cur].len2 = seg[cur].len3 = 0;
} else {
seg[cur].len3 = seg[lc].len3+seg[rc].len3
+seg[lc].len2+seg[rc].len2;
seg[cur].len2 = seg[lc].len1+seg[rc].len1;
seg[cur].len1 = Hash[r]-Hash[l-1]-seg[cur].len3-seg[cur].len2;
}
}
else {
if(l == r) seg[cur].len1 = seg[cur].len2 = seg[cur].len3 = 0;
else {
seg[cur].len1 = seg[lc].len1+seg[rc].len1;
seg[cur].len2 = seg[lc].len2+seg[rc].len2;
seg[cur].len3 = seg[lc].len3+seg[rc].len3;
}
}
}
void updata(int cur, int l, int r) {
if(L <= l && r <= R) {
seg[cur].cover += op;
pushup(cur, l, r);
return;
}
int mid = (l+r)>>1;
if(L <= mid) updata(lc, l, mid);
if(mid+1 <= R) updata(rc, mid+1, r);
pushup(cur, l, r);
}
int main(void)
{
int T, N;
scanf("%d", &T);
for(int cas = 1; cas <= T; ++cas) {
scanf("%d", &N);
int m = 0, m1 = 0, m2 = 0, maxny = 0;
for(int i = 0; i < N*6; i += 3) {
scanf("%d%d%d", &tp[i], &tp[i+1], &tp[i+2]);
Hash[m++] = tp[i];
Hash[m++] = tp[i+1];
}
sort(Hash, Hash+m); m = unique(Hash, Hash+m)-Hash;
int x1, x2, y1, y2, z1, z2;
for(int i = 0; i < N; ++i) {
x1 = lower_bound(Hash, Hash+m, tp[i*6])-Hash;
y1 = lower_bound(Hash, Hash+m, tp[i*6+1])-Hash;
x2 = lower_bound(Hash, Hash+m, tp[i*6+3])-Hash;
y2 = lower_bound(Hash, Hash+m, tp[i*6+4])-Hash;
z1 = tp[i*6+2]; z2 = tp[i*6+5];
tmp[m1++] = x1; tmp[m1++] = x2;
edge[++m2].l = y1+1; edge[m2].r = y2; edge[m2].xod = x1; edge[m2].zod = z1; edge[m2].val = 1; edge[m2].ed = x2;
edge[++m2].l = y1+1; edge[m2].r = y2; edge[m2].xod = x1; edge[m2].zod = z2; edge[m2].val = -1; edge[m2].ed = x2;
maxny = max(maxny, y1); maxny = max(maxny, y2);
}
sort(tmp, tmp+m1); m1 = unique(tmp, tmp+m1)-tmp;
sort(edge+1, edge+m2+1);
memset(X, 0, sizeof(X));
for(int i = 0; i < m1-1; ++i) X[tmp[i]] = tmp[i+1]; // X存的是x轴相邻的x坐标
for(int i = 0; i < (N<<3); ++i) seg[i].cover = seg[i].len1 = seg[i].len2 = seg[i].len3 = 0; // 树初始化
ll ans = 0, area; int cnt, pos;
printf("Case %d: ", cas);
for(int i = 0; i < m1-1; ++i) { // x轴
cnt = 0;
for(int j = 1; j <= m2; ++j) {
if(edge[j].xod <= tmp[i] && edge[j].ed > tmp[i]) tp[cnt++] = j;
}
area = 0;
for(int j = 0; j < cnt; ++j) {
L = edge[tp[j]].l; R = edge[tp[j]].r;
op = edge[tp[j]].val;
updata(1, 1, maxny);
if(j+1 < cnt)
area += (ll)seg[1].len3*(edge[tp[j+1]].zod-edge[tp[j]].zod);
}
ans += area*(Hash[tmp[i+1]]-Hash[tmp[i]]);
}
printf("%lld\n", ans);
}
return 0;
}