<更新提示>

入门看这边『线段树 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. Oracle 11.2.0.4打补丁

    所需补丁及高版本opatch 上传后将p6880880_112000_Linux-x86-64.zip解压覆盖$ORACLE_HOME/OPatch目录即可 [oracle@localhost OPa ...

  2. java常用IO流总结

  3. Java枚举的用法和原理深入

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10843644.html 一:枚举的用法 1.定义和组织常量 在JDK1.5之前,我们定义常量都是:publi ...

  4. Linux shell sed命令使用

    Linux处理文本文件的工具:     grep        过滤文件内容     sed            编辑文件内容     awk             正则表达式Regex      ...

  5. Linux学习之路-基础入门 20191104

    1.计算机组成 2.开发接口标准 ABI--(Application Binary Interface) ABI描述了应用程序与OS之间的底层接口,允许编译好的目标代码在使用兼容ABI的系统中无需改动 ...

  6. HDP 3.1.0 集成 Sqoop2

    HDP 3.1.0 集成 Sqoop2 本文原始地址:https://sitoi.cn/posts/65261.html 环境 由三台主机组成的 HDP 3.1.0 集群 配置好时间同步 步骤 下载 ...

  7. 部署LNMP应用平台

    一.LNMP应用平台概述 1.概述:LNMP代表的就是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构.Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/ ...

  8. centos7静黙安装Oracle11.2.0软件响应文件oracle_install.rsp

    oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v11_2_0 oracle.i ...

  9. ubuntu 1804安装详解

    我这边安装的是ubuntu server版本,大家安装时可以参考我这篇文件进行安装. 1.选择安装语言: 这里选择默认的"English"和“中文(简体)”都可以. 2.选择”安装 ...

  10. Android APK开发 Drawable文件夹下的自定义Drawable文件

    版本:2018/2/11 Drawable的分类 自定义Drawable SVG矢量图 个人总结的知识点外,部分知识点选自<Android开发艺术探索>-第六章 Drawable 1.Dr ...