题目大意

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

扫描线算法

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

线段树

节点维护的是什么

  水平线段在两个点[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 扫描线算法的更多相关文章

  1. [POJ1151]Atlantis

    [POJ1151]Atlantis 试题描述 There are several ancient Greek texts that contain descriptions of the fabled ...

  2. poj1151 Atlantis && cdoj 1600艾尔大停电 矩形面积并

    题目: Atlantis Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 23758   Accepted: 8834 Des ...

  3. ACM学习历程—POJ1151 Atlantis(扫描线 && 线段树)

    Description There are several ancient Greek texts that contain descriptions of the fabled island Atl ...

  4. POJ1151 Atlantis 【扫描线】

    Atlantis Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 16882   Accepted: 6435 Descrip ...

  5. POJ-1151 Atlantis 矩形面积并

    题目链接:http://poj.org/problem?id=1151 扫描线+离散+线段树,线段树每个节点保存的是离散后节点右边的线段. //STATUS:C++_AC_16MS_208KB #in ...

  6. poj1151 Atlantis (线段树+扫描线+离散化)

    有点难,扫描线易懂,离散化然后线段树处理有点不太好理解. 因为这里是一个区间,所有在线段树中更新时,必须是一个长度大于1的区间才是有效的,比如[l,l]这是一根线段,而不是区间了. AC代码 #inc ...

  7. POJ1151 Atlantis 水题 计算几何

    http://poj.org/problem?id=1151 想学一下扫描线线段树,结果写了道水题. #include<iostream> #include<cstdio> # ...

  8. POJ1151 Atlantis 线段树扫描线

    扫描线终于看懂了...咕咕了快三个月$qwq$ 对于所有的横线按纵坐标排序,矩阵靠下的线权值设为$1$,靠上的线权值设为$-1$,然后执行线段树区间加减,每次的贡献就是有效宽度乘上两次计算时的纵坐标之 ...

  9. poj1151 Atlantis——扫描线+线段树

    题目:http://poj.org/problem?id=1151 经典的扫描线问题: 可以用线段树的每个点代表横向被矩形上下边分割开的每一格,这样将一个矩形的出现或消失化为线段树上的单点修改: 每个 ...

随机推荐

  1. 树莓派-解决apt-get upgrade速度慢的方法[更换阿里云源]

    执行 apt-get upgrade 遇到速度慢的原因: 使用国外软件源 解决方法也很简单,将源换为国内环境即可,我选择阿里云 步骤 1.备份为 sources.list sudo cp /etc/a ...

  2. ajax-工作原理,包含技术,缺陷

    1:原理 2:包含技术 3:缺陷

  3. 实现三联tab切换特效

    当移动到菜单“小说”,“非小说”,“少儿”时菜单背景变换,并显示相应内容: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitio ...

  4. 2、scala条件控制与循环

    1.  if表达式 2.  句终结符.块表达式 3.  输入与输出 4.  循环 5.  高级for循环 1.  if表达式 if表达式的定义:scala中,表达式是有值的,就是if或者else中最后 ...

  5. mongodb 下载与安装文档

    MongoDB数据库安装及配置环境(windows10系统)   windows10系统下MongoDB的安装及环境配置: MongoDB的安装 下载地址: https://www.mongodb.c ...

  6. CMMI评估流程

    原文链接:http://www.cmmcn.com/new/cmmi-105.html 当前位置:首页 >> CMMI知识库 >> CMMI相关 >> CMMI评估 ...

  7. MQTTnet 的Asp.Net Core 认证事件的扩展

    MQTTnet 的数据接收 连接 等事件都很丰富, 唯独客户端连接验证不能依赖注入也不能很舒服的使用事件的方式, 因此MQTTnet.AspNetCoreEx 就出现了. 示例如下:在  public ...

  8. SSL证书在线申请和取回证书指南

    1.客服下单后用户会收到一封邮件,来验证接收证书的邮箱;如图1所示:只有完成此邮箱验证才能正常申请证书; 请注意:为了确保您或您的用户能正常收到WoSign证书管理系统自动发送的邮件,请一定要把系统发 ...

  9. 【剑指Offer】9、变态跳台阶

      题目描述:   一只青蛙一次可以跳上1级台阶,也可以跳上2级--它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳法.   解题思路:   当只有一级台阶时,f(1)=1:当有两级台阶时, ...

  10. 15.5.6 【Task实现细节】跟踪栈

    谈到栈帧(stack frame)时,可能会想到在方法中声明的局部变量.当然,可能还会注意到 一些隐藏的局部变量,如 foreach 循环中的迭代器.但栈上的内容不止这些,至少逻辑上是这样  . 很多 ...