zoukankan      html  css  js  c++  java
  • POJ1151 Atlantis 扫描线算法

    题目大意

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

    扫描线算法

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

    线段树

    节点维护的是什么

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

    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:只在最底层节点查询具体值,上层节点只存标记

    注意事项

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

    方法2:换一个定义

      在一个结点中,如果一个结点所负责的区间是一个水平线段的子区间,且该结点的父节点不是水平线段的子区间,则该结点所负责的区间为该水平线段的极大子区间。我们用CoverCnt表示:满足该节点维护的区间是一个水平线段的极大子区间的水平线段的个数。如果该结点的CoverCnt>0,则该结点负责的区间必然全部覆盖;如果CoverCnt==0,则该节点内的水平线段长就等于两个子区间的水平线段长之和。特别地,如果该节点是叶子结点,则它所负责的区间内的水平线段长就是0。

      根据此时CoverCnt的定义,我们不用(无法)PushDown。

    注意事项:

      如果要通过一个一个赋值来初始化线段树,循环终止条件不是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
    Total explored area: %.2lf
    
    ", ++caseCnt, ans);
    	}
    }
    

      

  • 相关阅读:
    JavaScript Array 属性 构造器 将数组值转为大写
    Eslint 配置及规则说明
    Vue基于vue-quill-editor富文本编辑器使用心得
    css display:flex 属性
    HTML5本地存储之localStorage、sessionStorage
    图片充满div
    微信小程序 Input框提交后清空
    前端简历怎么写
    响应式与自适应的区别
    JS点击子元素不触发父元素点击事件
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9170896.html
Copyright © 2011-2022 走看看