HDU 3642
题意
至少有2个以上不同地点 才存在宝藏.即3个以上的空间立体交叉覆盖出的体积 ,覆盖3次及以上x y z
包括整个库,意思就是给出顶点,观察样例知道是左上和右下的坐标 z<=500
.那么扫描法求出平面的面积,在处理题目给出的每一层.求的体积
思路
x
用来离散化后作为横轴建线段树,z
轴存下来枚举每一个层.x轴坐标
离散化后要去重,z轴坐标
要用来枚举所以也要去重.开始枚举每一层的z轴高度
,筛选出该层的所有点来求面积,将x轴上的横边
按照y轴的高度
来排序(扫描线标准做法). 建立线段树开始扫描,
3次覆盖如何用线段树维护
add
表示该区间被覆盖的次数 len1
表示该区间被覆盖次数为1的长度 len2
表示该区间被覆盖次数为2的长度 len3
表示该区间被覆盖次数>=3的长度
注意当前区间被覆盖次数不仅由len1,len2,len3组成,子区间如果也被覆盖,那么该区间不一样也被覆盖了吗? 根据覆盖次数的大小关系来维护 当前覆盖次数的覆盖长度=真实长度-上一层被覆盖的长度 ,例如求被覆盖2
次的长度,由于区间上面可能还覆盖一层(也就是区间被覆盖3
次),那么要减去被覆盖3
次的长度看代码也有解释
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 2005;
struct edge {
int l, r;
int h, f;//h,y坐标的高度,f上、下边
edge(){}
edge(int left,int right,int height,int flag) {
l = left; r = right; h = height; f = flag;
}
bool operator<(const edge& temp)const {
return h < temp.h;
}
}e[N<<1];
struct node {
int l, r;
int add; //覆盖次数
int len1, len2, len3;
}q[N<<3];//?
int x[N<<1], z[N<<1];
#define ls i<<1
#define rs i<<1|1
struct point {
int x, y, z;
point(){}
point(int tx,int ty,int tz) {
x = tx; y = ty; z = tz;
}
}a[N<<1];
void build(int i,int l,int r) {
q[i].l = l; q[i].r = r;
if (l == r) return;
int mid = (l+r) >> 1;
build(ls,l,mid);
build(rs,mid+1,r);
}
void pushup(int i) {
int r = q[i].r;
int l = q[i].l;
if (q[i].add >= 3) { //当前区间覆盖次数>=3
q[i].len3 = x[r + 1] - x[l]; //恢复右端+1,然后找到原始长度(总长度)
q[i].len1 = q[i].len2 = 0;
}
else if (q[i].add == 2) { //当前区间的覆盖次数==2
// 此时覆盖次数(=2) 加上子区间的1次(或1次以上)覆盖次数也能达到覆盖3次(以上)的效果
q[i].len3 = (q[ls].len1 + q[rs].len1) + (q[ls].len2+q[rs].len2) + (q[ls].len3+q[rs].len3);
q[i].len2 = x[r+1] - x[l] - q[i].len3; //求出覆盖2次的真实长度(总长度)-覆盖3层的长度
q[i].len1 = 0;
}
else if (q[i].add == 1) {
q[i].len3 = (q[ls].len2 + q[rs].len2) + (q[ls].len3 + q[rs].len3);
//len3的维护和上面len3的维护同理,1+2>=3
q[i].len2 = q[ls].len1 + q[rs].len1; //加上q[i].len2就会到覆盖3层的范围内
q[i].len1 = x[r + 1] - x[l] - q[i].len3 - q[i].len2;
}
else { //没有覆盖就从下到上更新
q[i].len3 = q[ls].len3 + q[rs].len3;
q[i].len2 = q[ls].len2 + q[rs].len2;
q[i].len1 = q[ls].len1 + q[rs].len1;
}
}
void update(int i, int l,int r,int val) {
if (l <= q[i].l && q[i].r <= r) {
q[i].add += val;
pushup(i);
return;
}
int mid = (q[i].l + q[i].r) >> 1;
if (r <= mid) update(ls,l,r,val);
else if (l > mid) update(rs,l,r,val);
else {
update(ls,l,mid,val);
update(rs,mid+1,r,val);
}
pushup(i);
}
int main() {
//freopen("a.txt", "r", stdin);
int times; cin >> times; int count = 0;
while (times--) {
int n; scanf("%d",&n);
int totx = 0, totz = 0, tot = 0;
int x1, y1, z1, x2, y2, z2;
for (int i = 1; i <= n; i++) {
scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
x[totx++] = x1; x[totx++] = x2;
z[totz++] = z1; z[totz++] = z2;
point t(x1, y1, z1); a[tot++] = t; // 第一个是左边点
t = point(x2, y2, z2); a[tot++] = t;// 第二个是右边点
}
printf("Case %d: ",++count);
if (n < 3) {
printf("0\n");
continue;
}
sort(x,x+totx);
sort(z,z+totz); // x和z坐标从小到大排个序
// 去重,让每一个坐标都对应一个值
totx = unique(x, x + totx) - x;
totz = unique(z, z + totz) - z;
long long ansxyz = 0;
for (int i = 0; i < totz-1; i++) {//访问至倒数第2层
//先将每一层的面积求出来,然后在*z轴高度求体积就行了
int k = 0;
for (int j = 0; j < tot; j += 2) { //遍历所有的点.每次扫描2个
if (a[j].z <= z[i] && z[i] < a[j + 1].z) {
// 上下边1,-1随便填,等下会按照高度排序,
// 题目数据先给左边的点,然后才给右边的点.所以a[j].x<a[j+1].x
e[k++] = edge(a[j].x,a[j+1].x,a[j].y,1);
e[k++] = edge(a[j].x,a[j+1].x,a[j+1].y,-1);
}
}
sort(e,e+k); // 根据y轴的值来排序,xy平面上的高度
memset(q,0,sizeof q); //清空线段树
build(1,0,totx-1);// x的范围[0,totx-1]
long long ansxy = 0;
// 扫描线开始扫描
for (int j = 0; j < k - 1; j++) {//[0,k-1]是该层上 的所有横边
//找到e[j].l e[j].r (离散化)也就是在线段树中对应的点
int l = lower_bound(x,x+totx,e[j].l) - x;
int r = lower_bound(x,x+totx,e[j].r) - x - 1; //扫描线规定.右端-1
update(1,l,r,e[j].f);
ansxy += (long long)q[1].len3 * (long long)(e[j + 1].h - e[j].h);
}
ansxyz += (long long)ansxy * (long long)(z[i+1] - z[i]); //面积*z轴高度=体积
}
printf("%I64d\n",ansxyz);
}
return 0;
}