Get The Treasury HDU - 3642
原题链接
考察:线段树(扫描线)
三维扫描线求重合>=3次的体积.
完全没想出来三维怎么推,结果发现是切片(.)
思路:
从小到大枚举z.求平面在枚举z范围内的重合>=3次的面积.ans累加即可.
这里求重合>=3次的不能用之前的懒标记法了.每个点遍历1000*2*N遍会TLE.因此采用更好的优化思路.
线段树维护的区间内再增加len1,len2,len3.分别表示覆盖1,2,>=3次的最大长度.关键是如何push_up
- 首先更新该区间的len1,这就是扫描线模板题的写法.
- 更新该区间的len2. 如果该区间cnt==1.那么len2 = 左子区间len1+右子区间len1.如果该区间cnt==2,那么直接返回长度.
- 更新len3.思路同len2.
这里我们用子区间的长度更新父区间,且不能重复,所以不能用push_down把懒标记往下传.
Code
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;//三维扫描线
typedef long long LL;
const int N = 1010,M = 500;
int n;
vector<int> v;
struct Segment{
int x,y1,y2,k,z1,z2;
bool operator<(const Segment& s)const{
return this->x<s.x;
}
}seg[N<<1];
struct Node{
int l,r,cnt,tag,len1,len2,len;
//只覆盖一次的长度,覆盖两次的长度,覆盖两次以上的长度
}tr[N<<3];
int get(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void build(int u,int l,int r)
{
tr[u] = {l,r,0,0,0,0,0};
if(l==r) return;
int mid = l+r>>1;
build(u<<1,l,mid); build(u<<1|1,mid+1,r);
}
int rget(int x)
{
return v[x-1];
}
void push_up(int u)
{
int l = tr[u].l,r= tr[u].r;
if(tr[u].cnt) tr[u].len1 = rget(tr[u].r+1)-rget(tr[u].l);
else if(tr[u].l!=tr[u].r) tr[u].len1 = tr[u<<1].len1+tr[u<<1|1].len1;
else tr[u].len1 = 0;
if(tr[u].cnt>1) tr[u].len2 = rget(tr[u].r+1)-rget(tr[u].l);
else if(tr[u].cnt==1&&l!=r) tr[u].len2 = tr[u<<1].len1+tr[u<<1|1].len1;
else if(l!=r) tr[u].len2 = tr[u<<1].len2+tr[u<<1|1].len2;
else tr[u].len2 = 0;
if(tr[u].cnt>2) tr[u].len = rget(tr[u].r+1)-rget(tr[u].l);
else if(l!=r&&tr[u].cnt==2) tr[u].len = tr[u<<1].len1+tr[u<<1|1].len1;
else if(l!=r&&tr[u].cnt==1) tr[u].len = tr[u<<1].len2+tr[u<<1|1].len2;
else if(l!=r) tr[u].len = tr[u<<1|1].len+tr[u<<1].len;
else tr[u].len = 0;
}
void modify(int u,int l,int r,int w)
{
if(tr[u].l>=l&&tr[u].r<=r)
{
tr[u].cnt+=w;
tr[u].tag+=w;
push_up(u);
return;
}
// push_down(u);
int mid = tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,w);
if(mid<r) modify(u<<1|1,l,r,w);
push_up(u);
}
int main()
{
int T,kcase = 0;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
LL ans = 0; v.clear();
for(int i=1,j=0;i<=n;i++)
{
int a,b,c,x,y,z;
scanf("%d%d%d%d%d%d",&a,&b,&c,&x,&y,&z);
seg[++j] = {a,b,y,1,c,z};
seg[++j] = {x,b,y,-1,c,z};
v.push_back(b),v.push_back(y);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
sort(seg+1,seg+2*n+1);
for(int z=-M;z<=M;z++)
{
int last = 0;
build(1,1,v.size());
for(int i=1;i<=2*n;i++)
if(seg[i].z1<=z&&seg[i].z2>z)//上表面那层不该被记录
{
ans+=(LL)tr[1].len*(seg[i].x-last);
int l = get(seg[i].y1),r = get(seg[i].y2);
modify(1,l,r-1,seg[i].k);
last = seg[i].x;
}
}
printf("Case %d: %lld\n",++kcase,ans);
}
return 0;
}