<更新提示>

入门看这边『线段树 Segment Tree』

<第一次更新>


<正文>

扫描线

扫描线是一种解决一类平面内统计问题的算法,通常会借助线段树来实现,我们通过一道例题来引入这个算法。

Atlantis

Description

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

Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist.

You (unwisely) volunteered to write a program that calculates this quantity.

Input Format

The input consists of several test cases. Each test case starts with a line containing a single integer n (1 <= n <= 100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0 <= x1 < x2 <= 100000;0 <= y1 < y2 <= 100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area. The input file is terminated by a line containing a single 0. Don't process it.

Output Format

For each test case, your program should output one section. The first line of each section must be "Test case #k", where k is the number of the test case (starting with 1). The second one must be "Total explored area: a", where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point. Output a blank line after each test case.

Sample Input

2
10 10 20 20
15 15 25 25.5
0

Sample Output

Test case #1
Total explored area: 180.00

解析

题目大意:给定平面内的\(n\)个矩形,求这\(n\)个矩形的面积并。

这是扫描线算法的引入问题,我们尝试设想有一条无限高的竖线左往右扫过这个并集图形,按照每一个矩形的的左右边界,我们可以将这个并集图形分为\(2n\)段,对于两两相邻的部分,我们可以分别计算面积,这样就得到了整个并集图形的面积。

如图,我们就是把每个矩形的左右边界提了出来,就变成了这样一些线段。

那么我们需要这些量化记录下来:每个四元组\((x,y_1,y_2,1/-1)\)分别代表了一条线段,\(x\)是线段的横坐标,\((y_1,y_2)\)是线段上下端点的纵坐标,\(1/-1\)代表了这条线段是矩形的左边界还是右边界。

显然,我们只需要把这些线段按照横坐标排序,对于一次遍历来说,两两线段之间的距离是已知的。那么我们需要解决的问题就是纵坐标的影响范围。

我们不妨把纵坐标都取出来,离散化映射到\([1,T]\)之间的\(T\)个整数值,并将这些纵坐标表示为\(T-1\)段,其中第\(i\)段代表了第\(i\)个纵坐标和第\(i+1\)个纵坐标之间的部分,然后,我们设立数组\(c_i\)代表第\(i\)段被覆盖的次数。

这样,我们就可以用如下的算法流程计算矩形的面积:

\(1.\) 对于每一个线段,将其的\(k\)值累加到这个线段对应的若干个纵坐标区间

\(2.\) 计算面积:所有\(T-1\)个纵坐标区间对应的\(c\)值大于零的就说明这些部分的区间还存在,将存在的区间的长度累加起来,乘上当前线段与下一条线段之间的横坐标之差就是这两条线段之间的面积。

显然,这里需要我们维护一个区间内的区间加法,区间求和,这个就是线段树的事情了。

由于本题中的区间修改成对出现,互相抵消,所以我们可以不写带有\(lazytag\)的线段树。我们在线段树的每一个节点上维护两个值\(cnt\)和\(len\),\(cnt\)代表这段区间被覆盖的次数,如果\(cnt>0\)则当前区间的\(len\)等于当前区间的纵坐标长度,反之\(len_p=len_{p*2}+len_{p*2+1}\)。那么对于每一次区间修改,我们直接在线段树上改\(cnt\)的值即可,并沿路更新\(len\)值即可。

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N=120;
struct line
{
double x,d,u;int flag;
}a[N<<1];
struct node
{
int l,r,cnt;
double len;
}v[N<<3];
int n,t,val[N<<1][2],T;
double raw[N<<2],ans;
inline bool compare(line p1,line p2){return p1.x<p2.x;};
inline void input(void)
{
for (int i=1;i<=n;i++)
{
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
a[2*i-1] = (line){x1,y1,y2,1};
a[2*i] = (line){x2,y1,y2,-1};
raw[++t] = y1 , raw[++t] = y2;
}
sort(a+1,a+2*n+1,compare);
}
inline void discrete(void)
{
sort(raw+1,raw+t+1);
t = unique(raw+1,raw+t+1) - (raw+1);
for (int i=1;i<=2*n;i++)
{
val[i][0] = lower_bound(raw+1,raw+t+1,a[i].d) - raw;
val[i][1] = lower_bound(raw+1,raw+t+1,a[i].u) - raw;
}
}
inline void updata(int p)
{
if (v[p].cnt>0) v[p].len = raw[v[p].r+1] - raw[v[p].l];
else if (v[p].l==v[p].r) v[p].len = 0;
else v[p].len = v[ p<<1 ].len + v[ p<<1|1 ].len;
}
inline void build(int p,int l,int r)
{
v[p].l = l , v[p].r = r;
if (l==r){v[l].cnt = v[l].len = 0; return;}
int mid = l+r >> 1;
build( p<<1 , l , mid );
build( p<<1|1 , mid+1 , r );
}
inline void modify(int p,int l,int r,int delta)
{
if (l<=v[p].l&&r>=v[p].r)
{
v[p].cnt += delta;
updata(p); return;
}
int mid = v[p].l+v[p].r >> 1;
if (l<=mid) modify( p<<1 , l , r , delta );
if (r>mid) modify( p<<1|1 , l , r , delta );
updata(p);
}
inline double query(void){return v[1].len;}
inline void solve(void)
{
for (int i=1;i<=2*n;i++)
{
modify(1,val[i][0],val[i][1]-1,a[i].flag);
ans += (a[i+1].x-a[i].x) * query();
}
}
int main(void)
{
while ( scanf("%d",&n) && n )
{
ans = t = 0;
input();
discrete();
build(1,1,t);
solve();
printf("Test case #%d\nTotal explored area: %.2lf\n\n",++T,ans);
}
return 0;
}

<后记>

『线段树及扫描线算法 Atlantis』的更多相关文章

  1. 『zkw线段树及其简单运用』

    阅读本文前,请确保已经阅读并理解了如下两篇文章: 『线段树 Segment Tree』 『线段树简单运用』 引入 这是一种由\(THU-zkw\)大佬发明的数据结构,本质上是经典的线段树区间划分思想, ...

  2. 『线段树 Segment Tree』

    更新了基础部分 更新了\(lazytag\)标记的讲解 线段树 Segment Tree 今天来讲一下经典的线段树. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间 ...

  3. 线段树 扫描线 L - Atlantis HDU - 1542 M - City Horizon POJ - 3277 N - Paint the Wall HDU - 1543

    学习博客推荐——线段树+扫描线(有关扫描线的理解) 我觉得要注意的几点 1 我的模板线段树的叶子节点存的都是 x[L]~x[L+1] 2 如果没有必要这个lazy 标志是可以不下传的 也就省了一个pu ...

  4. 线段树->面积并 Atlantis HDU - 1542

    题目链接:https://cn.vjudge.net/problem/HDU-1542 题目大意:求面积并 具体思路:我们首先把矩形分割成一横条一横条的,然后对于每一个我们给定的矩形,我们将储存两个点 ...

  5. hdu-1542 Atlantis(离散化+线段树+扫描线算法)

    题目链接: Atlantis Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 65536/32768 K (Java/Others) ...

  6. 『炸弹 线段树优化建图 Tarjan』

    炸弹(SNOI2017) Description 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸 时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi ...

  7. 洛谷 P6071 『MdOI R1』Treequery(LCA+线段树+主席树)

    题目链接 题意:给出一棵树,有边权,\(m\) 次询问,每次给出三个数 \(p,l,r\),求边集 \(\bigcap\limits_{i=l}^rE(p,i)\) 中所有边的权值和. 其中 \(E( ...

  8. 线段树---Atlantis

    题目网址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=110064#problem/A Description There are se ...

  9. hdu 1542 Atlantis(线段树,扫描线)

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

随机推荐

  1. 你的MES系统又失败了?正确的打开方式在这里

    都知道MES实施艰难,真正成功的很少:有人戏称:10个MES,7个失败.1个不死不活.1个伪成功,最后一个仍需努力. 导致MES实施失败的原因有很多,所谓“成功的MES是一样的,失败的MES各有各的失 ...

  2. python基础-面向对象编程之多态

    面向对象编程之多态以及继承.抽象类和鸭子类型三种表现形式 多态 定义:同一种类型的事物,不同的形态 作用: 多态也称之为"多态性".用于在不知道对象具体类型的情况下,统一对象调用方 ...

  3. lower_case_table_names和数据库在Linux和windows平台之间的相互迁移问题

    MySQL关于 lower_case_table_names 的文档 https://dev.mysql.com/doc/refman/5.7/en/identifier-case-sensitivi ...

  4. spring项目使用mave将第三方jar包打进war包中

    背景:一个标准的ssm项目,需要使用到第三方jar,现需要将项目发布到liunx的服务器上,将项目打成war包,第三方war包也需要打进去 首先是直接打包,发现第三方jar包并没有打进去......蛋 ...

  5. Chrome无界面启动使用

    Method1: from selenium import webdriver # 创建chrome参数对象opt = webdriver.ChromeOptions() # 把chrome设置成无界 ...

  6. 五、Xpath与lxml类库

    什么是XML XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据,而非显示数据 XML 的标签需要 ...

  7. HTTP中分块编码(Transfer-Encoding: chunked)

    转自: 妙音天女--分块传输编码~ 参考链接: HTTP MDN--HTTP协议 一.背景: 持续连接的问题:对于非持续连接,浏览器可以通过连接是否关闭来界定请求或响应实体的边界:而对于持续连接,这种 ...

  8. hbase表的高性能设计

    第7章 HBase优化 7.1 高可用 在HBase中Hmaster负责监控RegionServer的生命周期,均衡RegionServer的负载,如果Hmaster挂掉了,那么整个HBase集群将陷 ...

  9. Ingress对外暴露端口

    http,https端口 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: kubernetes-dashboard names ...

  10. 最全BT磁力搜索引擎索引(整理分享,不断更新...)

    最全BT磁力搜索引擎索引(整理分享,不断更新...) btkitty:http://cnbtkitty.com/(知名的BT磁力搜索,资源很多) idope.se:https://idope.se/( ...