这道题要求我们求得是在三维空间下 给定的n个长方体重叠三次以上的体积。而我们只需要先把z坐标轴拿掉,求三次以上的体积,就是在二维平面上找到重叠三次的面积再乘以这段面积上 存在的z的长度即可得到这段体积.
因此就把三维空间的问题转化到二维空间处理
重点就是三次覆盖情况下pushup函数的写法:
sum1代表覆盖一次,sum2两次,sum3 三次及以上
1:cover标记是大于2的情况 那么sum3就等于当前区间的长度,而sum1和sum2是等于0的因为此时没有两次以下的情况
2:cover标记正好覆盖到2次,此时sum3等于左右子树覆盖三次加左右子树覆盖2次的长度再加上左右子树覆盖一次的长度,因为这些区间长度加上2都可以能满足>=3,sum2等于当前区间的长度减去sum3(也就是大于等于3的)这一部分,sum1等于0
3:cover正好覆盖一次,那么sum3只能等于左右子树覆盖两次的区间长度和三次的长度,sum2等于左右子树覆盖一次的长度,sum1等于当前区间长度减去覆盖三次及以上和两次的长度
4:cover等于0 所有sum 此时都只能由他的左右子树来更新 也是就是都等于相对应的左右子树的和
void pushup(int rt,int l,int r)
{
if(cover[rt] > 2){//覆盖三层及以上时 sum3直接加 sum2和sum1此时都是0 因为直接覆盖的三层
sum3[rt] = x[r+1]-x[l];
sum2[rt] = sum1[rt] = 0;
return ;
}
else if(cover[rt] == 2){
if(l == r){
sum3[rt] = sum1[rt] = 0;
sum2[rt] = x[r+1]-x[l];
}
else{//此时覆盖两层 那么三层的可由三的左右子树还有二的左右子树再加一的左右子树组成 因为这些加上2都可以达到3及以上
sum3[rt]=sum3[rt<<1]+sum3[rt<<1|1]+sum2[rt<<1]+sum2[rt<<1|1]+sum1[rt<<1]+sum1[rt<<1|1];
sum2[rt] = x[r+1]-x[l]-sum3[rt];//两层的长度 等于总的区间长度 减去三层的长度
sum1[rt] = 0;
}
}
else if(cover[rt] == 1){
if(l == r){
sum3[rt] = sum2[rt] = 0;
sum1[rt] = x[r+1]-x[l];
return ;
}
else{
sum3[rt] = sum3[rt<<1]+sum3[rt<<1|1]+sum2[rt<<1]+sum2[rt<<1|1];
sum2[rt] = sum1[rt<<1]+sum1[rt<<1|1];
sum1[rt] = x[r+1]-x[l]-sum3[rt]-sum2[rt];
}
}
else{
if(l == r){
sum3[rt] = sum2[rt] = sum1[rt] = 0;
return ;
}
else{//相当于0 每个都只能通过 它自己的左右子树获得
sum3[rt]=sum3[rt<<1]+sum3[rt<<1|1];
sum2[rt]=sum2[rt<<1]+sum2[rt<<1|1];
sum1[rt]=sum1[rt<<1]+sum1[rt<<1|1];
}
}
}
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+7;
//三维空间的求体积并 只需要把z轴离散化后 挨个枚举z轴的区间 然后就变成了传统的二维空间的面积并
int sum1[MAXN],sum2[MAXN],sum3[MAXN];
int cover[MAXN];
int x[MAXN],z[MAXN];
int n;
struct Node
{
int l,r,h,f;
}line[MAXN];
struct point
{
int x,y,z;
};
struct node
{
point a,b;
}cube[MAXN];
// 理解正确的多层面积的讨论方式(思想)
void init()
{
memset(sum1,0,sizeof(sum1));
memset(sum2,0,sizeof(sum2));
memset(sum3,0,sizeof(sum3));
memset(cover,0,sizeof(cover));
}
bool cmp(Node a,Node b){
return a.h < b.h;
}
void pushup(int rt,int l,int r)
{
if(cover[rt] > 2){//覆盖三层及以上时 sum3直接加 sum2和sum1此时都是0 因为直接覆盖的三层
sum3[rt] = x[r+1]-x[l];
sum2[rt] = sum1[rt] = 0;
return ;
}
else if(cover[rt] == 2){
if(l == r){
sum3[rt] = sum1[rt] = 0;
sum2[rt] = x[r+1]-x[l];
}
else{//此时覆盖两层 那么三层的可由三的左右子树还有二的左右子树再加一的左右子树组成 因为这些加上2都可以达到3及以上
sum3[rt]=sum3[rt<<1]+sum3[rt<<1|1]+sum2[rt<<1]+sum2[rt<<1|1]+sum1[rt<<1]+sum1[rt<<1|1];
sum2[rt] = x[r+1]-x[l]-sum3[rt];//两层的长度 等于总的区间长度 减去三层的长度
sum1[rt] = 0;
}
}
else if(cover[rt] == 1){
if(l == r){
sum3[rt] = sum2[rt] = 0;
sum1[rt] = x[r+1]-x[l];
return ;
}
else{
sum3[rt] = sum3[rt<<1]+sum3[rt<<1|1]+sum2[rt<<1]+sum2[rt<<1|1];
sum2[rt] = sum1[rt<<1]+sum1[rt<<1|1];
sum1[rt] = x[r+1]-x[l]-sum3[rt]-sum2[rt];
}
}
else{
if(l == r){
sum3[rt] = sum2[rt] = sum1[rt] = 0;
return ;
}
else{//相当于0 每个都只能通过 它自己的左右子树获得
sum3[rt]=sum3[rt<<1]+sum3[rt<<1|1];
sum2[rt]=sum2[rt<<1]+sum2[rt<<1|1];
sum1[rt]=sum1[rt<<1]+sum1[rt<<1|1];
}
}
}
void update(int rt,int l,int r,int ul,int ur,int x)
{
if(l>=ul&&r<=ur){
cover[rt]+=x;
pushup(rt,l,r);
return ;
}
int mid = (l+r)>>1;
if(ul <= mid) update(rt<<1,l,mid,ul,ur,x);
if(ur > mid) update(rt<<1|1,mid+1,r,ul,ur,x);
pushup(rt,l,r);
}
int main()
{
int t,cas = 0;
scanf("%d",&t);
while(t--){
ll ans = 0;
int cntx = 0,cntz = 0;//需要分别对x和z离散化
scanf("%d",&n);
for(int i = 1;i <= n;i ++){
scanf("%d%d%d%d%d%d",&cube[i].a.x,&cube[i].a.y,&cube[i].a.z,&cube[i].b.x,&cube[i].b.y,&cube[i].b.z);
x[++cntx] = cube[i].a.x;
z[++cntz] = cube[i].a.z;
x[++cntx] = cube[i].b.x;
z[++cntz] = cube[i].b.z;
}
if(n < 3){
printf("Case %d: 0\n",++cas);
continue;
}
sort(x+1,x+1+cntx);
sort(z+1,z+1+cntz);
cntx = unique(x+1,x+1+cntx)-x-1;
cntz = unique(z+1,z+1+cntz)-z-1;
// 枚举z坐标 每次把在这个范围内的矩形找出 每个循环中相当于做一次二维的扫描 就和前几道题一样了
for(int i = 1;i < cntz;i ++){
int tot = 0;
for(int k = 1;k <= n;k ++){
if(cube[k].a.z<=z[i]&&cube[k].b.z>z[i]){
int x1 = cube[k].a.x,x2 = cube[k].b.x;
int y1 = cube[k].a.y,y2 = cube[k].b.y;
line[++tot].l = x1,line[tot].r = x2,line[tot].h = y1,line[tot].f = 1;
line[++tot].l = x1,line[tot].r = x2,line[tot].h = y2,line[tot].f = -1;
}
}
init();// 这个就相当于建树 初始化了
sort(line+1,line+1+tot,cmp);
ll res = 0;
for(int k = 1;k < tot;k ++){
int l = lower_bound(x+1,x+1+cntx,line[k].l)-x;
int r = lower_bound(x+1,x+1+cntx,line[k].r)-x-1;
//printf("%d %d\n",l,r);
update(1,1,cntx,l,r,line[k].f);
res += (ll)sum3[1]*(line[k+1].h-line[k].h);
}
ans += res * (z[i+1]-z[i]);
}
printf("Case %d: %lld\n",++cas,ans);
}
return 0;
}