POJ1151 Atlantis 扫描线算法

题目大意

  给出几个矩形对角端点坐标,求这些矩形整体覆盖的面积。

扫描线算法

  整个平面被每个矩形的长边所在直线(以后简称“长线”)分成了几个部分,而整体覆盖面积则为每相邻的两个长线间夹的长度(以后简称“夹长”)以及长线所对应矩形长边在长线(以后简称“长线段”)中所占的长度之积的和。于是我们假设有一个扫描线从下往上扫。由排序得到夹长很容易,但是长线段长就要用线段树了。

线段树

节点维护的是什么

  长线段在两个点[l,r]之间所占的长度。

l,r是double怎么办?

  运用离散化将double映射成整型排名。

注意事项

  在把SortedData的初值全部设为0时,要考虑到l,r可能是0。这样在设置rank数组时,0所对应的排名就成了0,最后线段树爆栈导致RE。所以要让SortedData[0] = -1。

离散化后,如何体现长线段原先的长度?

  既然结点维护的格子是离散化后的排名,要想知道原先的长度,离散回去即可。

线段树维护的是格子,而我们要维护的是点,怎么办?

  把两点之间的区间作为线段树所维护的格子即可。线段树中格子p表示离散化后排名为p的点和p+1两个点之间的区间。(所以对线段树操作时,输入的r要-1)。

注意事项

  为了使鲁棒性更高,防止出现“一个矩形就是一个竖直线”导致ar==al-1的情况,此时要及时返回。

包含于一段的区间的长线段的个数CoverCnt不满足相加性,怎么办?

方法1:只在最底层节点查询具体值,上层节点只存标记

  此方法没有方法2快(虽然没有事实证明)。

注意事项

  判断是否PushDown要看该结点是否有DeltaTag标记,而一个结点是否在最底层这个标记无论是否有DeltaTag都要设置。

方法2:换一个定义

  在一个结点中,我们用CoverCnt表示包含结点负责区间的长线段个数。如果该结点的CoverCnt>0,则该结点负责的区间必然全部覆盖;如果CoverCnt==0,则该节点内的长线段长就等于两个子区间的长线段长之和。特别地,如果该节点是叶子结点,则它所负责的区间内的长线段长就是0。

注意事项:

  如果要通过一个一个赋值来初始化线段树,循环终止条件不是N,要初始化就彻底一点。

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

const int MAX_AREA = 110, MAX_LINE = MAX_AREA * 2,
MAX_P = MAX_LINE * 2, MAX_NODE = MAX_P * 4;

struct HorLine
{
	double L, R, H;
	int Flag;

	HorLine(double l = 0, double r = 0, double h = 0, int flag = 0) :L(l), R(r), H(h), Flag(flag) {}

	bool operator < (const HorLine& a)const
	{
		return H < a.H;
	}
}_lines[MAX_LINE];

struct Discret
{
private:
	double SortedData[MAX_P];
	int Rank[MAX_P];
	int N;

	int LowerBoundByVal(int l, int r, double k)
	{
		while (l < r)
		{
			int mid = (l + r) / 2;
			if (k <= SortedData[mid])
				r = mid;
			else
				l = mid + 1;
		}
		return l;
	}

	int LowerBoundByRank(int l, int r, int k)
	{
		while (l < r)
		{
			int mid = (l + r) / 2;
			if (k <= Rank[mid])
				r = mid;
			else
				l = mid + 1;
		}
		return l;
	}

public:
	int RankCnt;

	void Init(double *a, int n)
	{
		N = n;
		memset(SortedData, 0, sizeof(SortedData));
		memset(Rank, 0, sizeof(Rank));
		for (int i = 1; i <= n; i++)
			SortedData[i] = a[i];
		sort(SortedData + 1, SortedData + n + 1);
		SortedData[0] = -1;
		int curRank = 0;
		for (int i = 1; i <= n; i++)
		{
			if (SortedData[i] == SortedData[i - 1])
				Rank[i] = curRank;
			else
				Rank[i] = ++curRank;
		}
		RankCnt = curRank;
	}

	int GetRankByVal(double val)
	{
		return Rank[LowerBoundByVal(1, N, val)];
	}

	double GetValByRank(int rank)
	{
		return SortedData[LowerBoundByRank(1, N, rank)];
	}
}g;

struct RangeTree
{
private:
	int N;

	struct Node
	{
		Discret *Dis;
		int CoverCnt;
		double CoverLen;
		double Len;

		Node():Len(-1){}

		void GetLen(int l, int r)
		{
			if(Len < 0)
				Len = Dis->GetValByRank(r + 1) - Dis->GetValByRank(l);
		}

	}_nodes[MAX_NODE];

	void UpdateNode(int cur, int l, int r, int delta)
	{
		_nodes[cur].GetLen(l, r);
		_nodes[cur].CoverCnt += delta;
		if (_nodes[cur].CoverCnt > 0)
			_nodes[cur].CoverLen = _nodes[cur].Len;
		else if (l == r)
			_nodes[cur].CoverLen = 0;
		else
			_nodes[cur].CoverLen = _nodes[cur * 2].CoverLen + _nodes[cur * 2 + 1].CoverLen;
	}

	void Update(int cur, int sl, int sr, int al, int ar, int delta)
	{
		if (al <= sl && sr <= ar)
		{
			UpdateNode(cur, sl, sr, delta);
			return;
		}
		int mid = (sl + sr) / 2;
		if (al <= mid)
			Update(cur * 2, sl, mid, al, ar, delta);
		if (ar > mid)
			Update(cur * 2 + 1, mid + 1, sr, al, ar, delta);
		UpdateNode(cur, sl, sr, 0);
	}

	double Query(int cur, int sl, int sr, int al, int ar)
	{
		if (al <= sl && sr <= ar)
			return _nodes[cur].CoverLen;
		int mid = (sl + sr) / 2;
		double ans = 0;
		if (al <= mid)
			ans += Query(cur * 2, sl, mid, al, ar);
		if (ar > mid)
			ans += Query(cur * 2, mid + 1, sr, al, ar);
		return ans;
	}

public:
	RangeTree(Discret* dis)
	{
		for (int i = 1; i <= MAX_NODE - 1; i++)
			_nodes[i].Dis = dis;
	}

	void Init(int n)
	{
		N = n;
		for (int i = 1; i <= MAX_NODE - 1; i++)
			_nodes[i].CoverCnt = 0, _nodes[i].CoverLen = 0, _nodes[i].Len = -1;
	}

	void Update(int l, int r, int delta)
	{
		if (r < l)
			return;
		Update(1, 1, N, l, r, delta);
	}

	double Query(int l, int r)
	{
		if (r < l)
			return 0;
		return Query(1, 1, N, l, r);
	}
}h(&g);

int main()
{
	static double pExist[MAX_LINE * 2];
	int AreaCnt;
	int caseCnt = 0;
	while (~scanf("%d", &AreaCnt) && AreaCnt)
	{
		memset(pExist, 0, sizeof(pExist));
		memset(_lines, 0, sizeof(_lines));
		for (int i = 1; i <= AreaCnt; i++)
		{
			double x1, x2, y1, y2;
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			if (y1 < y2)
				swap(y1, y2);
			_lines[i * 2 - 1] = HorLine(x1, x2, y1, -1);
			_lines[i * 2] = HorLine(x1, x2, y2, 1);
			pExist[i * 2 - 1] = x1;
			pExist[i * 2] = x2;
		}
		sort(_lines + 1, _lines + AreaCnt * 2 + 1);
		g.Init(pExist, AreaCnt * 2);
		h.Init(g.RankCnt);
		double ans = 0;
		for (int i = 1; i <= AreaCnt * 2; i++)
		{
			ans += h.Query(1, g.RankCnt) * (_lines[i].H - _lines[i - 1].H);
			h.Update(g.GetRankByVal(_lines[i].L), g.GetRankByVal(_lines[i].R) - 1, _lines[i].Flag);
		}
		printf("Test case #%d\nTotal explored area: %.2lf\n\n", ++caseCnt, ans);
	}
}

  

猜你喜欢

转载自www.cnblogs.com/headboy2002/p/9170896.html