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\nTotal explored area: %.2lf\n\n", ++caseCnt, ans);
}
}
POJ1151 Atlantis 扫描线算法的更多相关文章
- [POJ1151]Atlantis
[POJ1151]Atlantis 试题描述 There are several ancient Greek texts that contain descriptions of the fabled ...
- poj1151 Atlantis && cdoj 1600艾尔大停电 矩形面积并
题目: Atlantis Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 23758 Accepted: 8834 Des ...
- ACM学习历程—POJ1151 Atlantis(扫描线 && 线段树)
Description There are several ancient Greek texts that contain descriptions of the fabled island Atl ...
- POJ1151 Atlantis 【扫描线】
Atlantis Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 16882 Accepted: 6435 Descrip ...
- POJ-1151 Atlantis 矩形面积并
题目链接:http://poj.org/problem?id=1151 扫描线+离散+线段树,线段树每个节点保存的是离散后节点右边的线段. //STATUS:C++_AC_16MS_208KB #in ...
- poj1151 Atlantis (线段树+扫描线+离散化)
有点难,扫描线易懂,离散化然后线段树处理有点不太好理解. 因为这里是一个区间,所有在线段树中更新时,必须是一个长度大于1的区间才是有效的,比如[l,l]这是一根线段,而不是区间了. AC代码 #inc ...
- POJ1151 Atlantis 水题 计算几何
http://poj.org/problem?id=1151 想学一下扫描线线段树,结果写了道水题. #include<iostream> #include<cstdio> # ...
- POJ1151 Atlantis 线段树扫描线
扫描线终于看懂了...咕咕了快三个月$qwq$ 对于所有的横线按纵坐标排序,矩阵靠下的线权值设为$1$,靠上的线权值设为$-1$,然后执行线段树区间加减,每次的贡献就是有效宽度乘上两次计算时的纵坐标之 ...
- poj1151 Atlantis——扫描线+线段树
题目:http://poj.org/problem?id=1151 经典的扫描线问题: 可以用线段树的每个点代表横向被矩形上下边分割开的每一格,这样将一个矩形的出现或消失化为线段树上的单点修改: 每个 ...
随机推荐
- Laravel5.1学习笔记7 视图
视图 (View) 基本用法 传递数据到视图 在多个视图中分享数据 视图组件 #基本用法 视图里面包含了你应用程序所提供的 HTML 代码,并且提供一个简单的方式来分离控制器和网页呈现上的逻辑.视 ...
- https 结合使用 对称加密和非对称加密
(一)对称加密(Symmetric Cryptography) ---共享密钥加密 对称加密是最快速.最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥( ...
- Android嵌入式(初稿)--路漫漫其修远兮,吾将上下而求索
- Visual C++ 6.0的界面介绍
双击Visual C++ 6.0安装目录下的文件启动Visual C++ 6.0,通过“文件”→“新建”可新建一个Win32 Console Application项目.创建好项目后,显示Visu ...
- Deutsch lernen (08)
1. empfehlen - empfahl - hat empfohlen 推荐:劝说,劝告 Können Sie mir einen guten Artz empfehlen? jemand e ...
- Embedded之Stack之一
1 Intro When a program starts executing, a certain contiguous section of memory is set aside for the ...
- dubbo之隐式参数
隐式参数 可以通过 RpcContext 上的 setAttachment 和 getAttachment 在服务消费方和提供方之间进行参数的隐式传递. 在服务消费方端设置隐式参数 setAttach ...
- 国外AI界牛人主页 及资源链接
感觉 好博客要收集,还是贴在自己空间里难忘!!! 原文链接:http://blog.csdn.net/hitwengqi/article/details/7907366 http://people.c ...
- Appium Desired Capabilities信息配置
编写APPium脚本,必须要配置Desired Capabilities信息 Desired Capabilities 在启动 session 的时候是必须提供的. Desired Capabilit ...
- Fish:Linux中比bash或zsh更好用的Shell
Fish是一个智能且用户友好的命令行shell,适用于macOS,Linux和其他家族.fish包含语法突出显示.根据你键入字符自动提示autosuggest-as-type和花式选项卡完成等功能,无 ...