題意:給定一堆長方體(< =1000),求重疊了大於等於3次的體積。
思路:線段樹掃描線,或者說,掃描面。
這題 |X|,|Y|<=10^6 , |Z|<=500
由於Z相對較小,所以,以垂直Z軸的面來掃描整個圖形。
每一個面再用線段樹掃描線,記錄重疊了3次或以上的面積,然後乘以Z軸的變化值,就可以得到重疊了3次或以上的體積。
用到的數據結構分四部分:
一:離散化y值
用一個Rank數組解決,排序去重複,然後二分求每個值的離散後的值。
二:儲存線段信息
按z 值排序即可,要儲存入邊,出邊等信息。
三:儲存同層中掃描線時的線段信息
需按x值排序。
主要是不同的層用到的線段不一樣,需要很多加入和刪除,寫平衡樹會很麻煩。
所以乾脆直接用IN數組來標記每一個線段是否需要考慮,然後每次都線性掃全部線段。
四:線段樹
線段樹進行區間覆蓋,維護重疊了3次或以上的線段長度。
節點中,C:覆蓋次數 CL[K]:重疊了大於等於K次的線段長度。
代碼如下:
//1170MS 1832K
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 2007
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define LL long long
using namespace std;
int n,N;
//一: 離散化y值
//二:線段信息 :(Z,X,Y1,Y2,出/入邊,出入/層) 排序:(Z,X)
//三:同層線段(層間變化避免重新排序)---節點:(X,Y1,Y2,出/入邊) 排序:(X)
//四:線段樹 節點:(C,CL[4])
//一: 離散化y值 2*n
int Rank[maxn],Rn;
int InitRank(){//排序去重
sort(Rank,Rank+N);Rn=0;
for(int i=1;i<N;++i){if(Rank[i]!=Rank[i-1]) Rank[++Rn]=Rank[i];}
}
int GetRank(double x){
int L=0,R=Rn;//[L,R] first >= x
while(L^R){
int M=(L+R)>>1;
if(Rank[M]<x) L=M+1;
else R=M;
}
return L;
}
//二:線段信息 4*n
int Ln;
struct Lines{
int z,x,y1,y2,xin,zin,id;
Lines(){}
Lines(int z,int x,int y1,int y2,int xin,int zin,int id):z(z),x(x),y1(y1),y2(y2),xin(xin),zin(zin),id(id){}
bool operator <(const Lines &B)const{return z < B.z;}
}Line[maxn<<1];
//三:同層線段 2*n
struct Node{
int x,y1,y2,xin,id;
Node(){}
Node(int x,int y1,int y2,int xin,int id):x(x),y1(y1),y2(y2),xin(xin),id(id){}
bool operator < (const Node &B)const{return x < B.x || x==B.x && id<B.id;}
bool operator ==(const Node &B)const{return x==B.x&&y1==B.y1&&y2==B.y2&&xin==B.xin&&id==B.id;}
}K[maxn];
int Kn;
bool In[maxn];
int GetR(Node X){
int L=1,R=Kn;//[L,R] first >= X
while(L^R){
int M=(L+R)>>1;
if(K[M]<X) L=M+1;
else R=M;
}
return L;
}
void Insert(Node X){In[GetR(X)]=1;}
void Delete(Node X){In[GetR(X)]=0;}
//四:線段樹 節點:(C,CL[4])
struct Nodes{
int C;//Cover
int CL[4];//CoverLength[0~3]
}ST[maxn<<3];
void PushUp(int rt){
for(int i=1;i<=3;++i){
if(ST[rt].C < i) ST[rt].CL[i]=ST[rt<<1].CL[i-ST[rt].C]+ST[rt<<1|1].CL[i-ST[rt].C];
else ST[rt].CL[i]=ST[rt].CL[0];
}
}
void Build(int l,int r,int rt){
if(l==r){
ST[rt].C=0;
ST[rt].CL[0]=Rank[l]-Rank[l-1];
ST[rt].CL[1]=ST[rt].CL[2]=ST[rt].CL[3]=0;
return;
}
int m=(l+r)>>1;
Build(ls);
Build(rs);
ST[rt].C=0;
ST[rt].CL[0]=ST[rt<<1].CL[0]+ST[rt<<1|1].CL[0];
PushUp(rt);
}
void Update(int L,int R,int C,int l,int r,int rt){
if(L <= l && r <= R){
ST[rt].C+=C;
if(l==r){
ST[rt].CL[1]=ST[rt].C>=1?ST[rt].CL[0]:0;
ST[rt].CL[2]=ST[rt].C>=2?ST[rt].CL[0]:0;
ST[rt].CL[3]=ST[rt].C>=3?ST[rt].CL[0]:0;
}
else PushUp(rt);
return;
}
int m=(l+r)>>1;
if(L <= m) Update(L,R,C,ls);
if(R > m) Update(L,R,C,rs);
PushUp(rt);
}
int main(void)
{
int t;scanf("%d",&t);
for(int Case=1;Case<=t;++Case){
scanf("%d",&n);N=Ln=Kn=0;
for(int i=0;i<n;++i){
int x1,y1,z1,x2,y2,z2;
scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
//離散化
Rank[N++]=y1;
Rank[N++]=y2;
//存值部分
Line[Ln++]=Lines(z1,x1,y1,y2,1,1,i<<1);
Line[Ln++]=Lines(z1,x2,y1,y2,-1,1,i<<1|1);
Line[Ln++]=Lines(z2,x1,y1,y2,1,-1,i<<1);
Line[Ln++]=Lines(z2,x2,y1,y2,-1,-1,i<<1|1);
//K
K[++Kn]=Node(x1,y1,y2,1,i<<1);
K[++Kn]=Node(x2,y1,y2,-1,i<<1|1);
}
InitRank();
sort(K+1,K+Kn+1);
sort(Line,Line+Ln);
memset(In,0,sizeof(In));
LL Z,PreZ=Line[0].z,PreArea=0,ANS=0;
int I=0;
while(I < Ln){//掃描面
Z=Line[I].z;
ANS+=(Z-PreZ)*PreArea;
while(I < Ln && Line[I].z==Z){
//修改掃描邊列表
if(~Line[I].zin) Insert(Node(Line[I].x,Line[I].y1,Line[I].y2,Line[I].xin,Line[I].id));
else Delete(Node(Line[I].x,Line[I].y1,Line[I].y2,Line[I].xin,Line[I].id));
I++;
}
Build(1,Rn,1);
int J=1;
LL X,PreX=K[0].x,PreLen=0,Area=0;
while(J <= Kn){//掃描線
X=K[J].x;
Area+=(X-PreX)*PreLen;
while(J <= Kn && K[J].x==X){
//修改線段
if(In[J]) Update(GetRank(K[J].y1)+1,GetRank(K[J].y2),K[J].xin,1,Rn,1);
J++;
}
PreX=X;
PreLen=ST[1].CL[3];
}
//更新PreZ,PreArea
PreZ=Z;
PreArea=Area;
}
printf("Case %d: %I64d\n",Case,ANS);
}
return 0;
}