您的位置:首页 > 理论基础 > 数据结构算法

HDU 3642 Get The Treasury (线段树扫描线进阶--求长方体重叠3次或以上的体积)

2015-08-18 23:44 656 查看
题意:给定一堆长方体(< =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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线段树 数据结构