hdu 1255(线段树求重叠面积)

扫描线求矩形重叠面积:http://www.cnblogs.com/scau20110726/archive/2013/04/12/3016765.html

                                       http://www.tuicool.com/articles/6Zf6J3


这题参考:http://www.cppblog.com/menjitianya/archive/2011/03/30/143043.html


解题思路:线段树+离散化

首先我们将每个矩形的纵向边投影到Y轴上,这样就可以把矩形的纵向边看成一个闭区间,用线段树来维护这些矩形边的并。现在求的是矩形的面积并,于是可以枚举矩形的x坐标,然后检测当前相邻x坐标上y方向的合法长度,两者相乘就是其中一块面积,枚举完毕后就求得了所有矩形的面积并。  我的线段树结点描述保存了以下信息:区间的左右端点、结点所在数组编号(因为采用静态结点可以大大节省申请空间的时间)、该结点被竖直线段完全覆盖的次数Cover和当前结点覆盖一次的y方向长度yOnce和当前结点覆盖多次的y方向长度yMore。
    其实和矩形面积并的唯一差别就是在计算Cover后的Update函数,更新yOnce和yMore的值,分情况讨论:
1. 当nCover>1时,yOnce = 0; yMore = 区间实际长度;
2. 当nCover=1时,yMore = 两棵子树的yOnce+yMore; 
                 yOnce = 区间实际长度 - yMore;
3. 当nCover=0时,如果是叶子结点 yOnce = 0; yMore = 0;
                 否则 
                 yOnce = 两棵子树的yOnce和;
                 yMore = 两棵子树的yMore和;


这题还是先要把扫描法看懂了,剩下的就好办了。


AC:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn = 2200;
const double eps = 1e-6;
double edge[maxn],tmp[maxn];
int tmpsize,size;

struct Segment
{
	int p,l,r;
	int nCover;
	double yOne,yMore;

	void Update()
	{
		if(nCover > 1)
		{
			yOne = 0;
			yMore = edge[r] - edge[l];
		}
		else if(nCover == 1)
		{
			yMore = tree[p<<1].yMore + tree[p<<1].yOne + tree[p<<1|1].yMore + tree[p<<1|1].yOne;
			yOne = edge[r] - edge[l] - yMore;
		}
		else
		{
			if(l + 1 == r) //叶子节点,无法拓展
			{
				yOne = yMore = 0;
			}
			else
			{
				yOne = tree[p<<1].yOne + tree[p<<1|1].yOne;
				yMore = tree[p<<1].yMore + tree[p<<1|1].yMore;
			}
		}
	}
}tree[maxn<<2];

struct Line
{
	double x,y1,y2;
	int val;

	Line(){ }
	Line(double _x,double _y1,double _y2,double _val)
	{
		x = _x;
		y1 = _y1;
		y2 = _y2;
		val = _val;
	}
};
vector<Line> vec;

bool cmp(Line a, Line b)
{
	return a.x < b.x;
}

void DiscretData()
{
	sort(tmp,tmp+tmpsize);
	size = 0;
	edge[++size] = tmp[0];
	for(int i = 1; i < tmpsize; i++)
		if(fabs(tmp[i] - tmp[i-1]) > eps)
			edge[++size] = tmp[i];
}

void build(int o,int l,int r)
{

	tree[o].l = l, tree[o].r = r;
	tree[o].nCover = tree[o].yMore = tree[o].yOne = 0;
	tree[o].p = o;	
	if(l+1 == r || l == r) return;
	int mid = (l + r) >> 1;
	build(o<<1,l,mid);
	build(o<<1|1,mid,r);	//这里不能是mid+1,因为算的是线段长度,如果是mid+1,会变成1-2和3-4的区间,中间2-3这一段就无法计算了
}

void insert(int o,int l,int r,int val)
{
	if(l <= tree[o].l && tree[o].r <= r)
	{
		tree[o].nCover += val;
		tree[o].Update();
		return;
	}
	int mid = (tree[o].l + tree[o].r) >> 1;
	if(mid > l) insert(o<<1,l,r,val);
	if(mid < r) insert(o<<1|1,l,r,val);
	tree[o].Update();
}

int bisearch(double x)
{
	int l = 1,r = size,mid;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(fabs(edge[mid] - x) < eps)
			return mid;
		else if(x > edge[mid])
			l = mid + 1;
		else r = mid - 1;
	}
}

int main()
{
	int t,n;
	cin >> t;
	while(t--)
	{
		cin >> n;
		tmpsize = 0;
		vec.clear();
		for(int i = 1; i <= n; i++)
		{
			double x0,x1,y0,y1;
			cin >> x0 >> y0 >> x1 >> y1;
			vec.push_back(Line(x0,y0,y1,1));
			vec.push_back(Line(x1,y0,y1,-1));
			tmp[tmpsize++] = y0;
			tmp[tmpsize++] = y1;
		}
		sort(vec.begin(),vec.end(),cmp);
		DiscretData();
		build(1,1,size);
		double ans = 0;
		for(int i = 0; i < vec.size(); i++)
		{
			if(i > 0)
				ans += (vec[i].x - vec[i-1].x) * tree[1].yMore;
			int l = bisearch(vec[i].y1);
			int r = bisearch(vec[i].y2);
			insert(1,l,r,vec[i].val);
		}
		printf("%.2lf\n",ans);
	}
	return 0;
}



你可能感兴趣的:(数据结构)