Get The Treasury HDU - 3642

原题链接
考察:线段树(扫描线)
  三维扫描线求重合>=3次的体积.
  完全没想出来三维怎么推,结果发现是切片(.)
思路:
  从小到大枚举z.求平面在枚举z范围内的重合>=3次的面积.ans累加即可.
  这里求重合>=3次的不能用之前的懒标记法了.每个点遍历1000*2*N遍会TLE.因此采用更好的优化思路.
  线段树维护的区间内再增加len1,len2,len3.分别表示覆盖1,2,>=3次的最大长度.关键是如何push_up

  1. 首先更新该区间的len1,这就是扫描线模板题的写法.
  2. 更新该区间的len2. 如果该区间cnt==1.那么len2 = 左子区间len1+右子区间len1.如果该区间cnt==2,那么直接返回长度.
  3. 更新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;
}

posted @ 2021-05-21 23:41  acmloser  阅读(26)  评论(0编辑  收藏  举报