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;
}
思路:线段树扫描线,或者说,扫描面。
这题 |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;
}
相关文章推荐
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构揭秘一
- 数据结构之Treap详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)
- java数据结构和算法学习之汉诺塔示例
- Java数据结构及算法实例:三角数字
- Java数据结构之简单链表的定义与实现方法示例
- 数据结构之AVL树详解
- qqwry.dat的数据结构图文解释第1/2页
- JavaScript中数据结构与算法(五):经典KMP算法