『线段树及扫描线算法 Atlantis』
<更新提示>
入门看这边『线段树 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』的更多相关文章
- 『zkw线段树及其简单运用』
阅读本文前,请确保已经阅读并理解了如下两篇文章: 『线段树 Segment Tree』 『线段树简单运用』 引入 这是一种由\(THU-zkw\)大佬发明的数据结构,本质上是经典的线段树区间划分思想, ...
- 『线段树 Segment Tree』
更新了基础部分 更新了\(lazytag\)标记的讲解 线段树 Segment Tree 今天来讲一下经典的线段树. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间 ...
- 线段树 扫描线 L - Atlantis HDU - 1542 M - City Horizon POJ - 3277 N - Paint the Wall HDU - 1543
学习博客推荐——线段树+扫描线(有关扫描线的理解) 我觉得要注意的几点 1 我的模板线段树的叶子节点存的都是 x[L]~x[L+1] 2 如果没有必要这个lazy 标志是可以不下传的 也就省了一个pu ...
- 线段树->面积并 Atlantis HDU - 1542
题目链接:https://cn.vjudge.net/problem/HDU-1542 题目大意:求面积并 具体思路:我们首先把矩形分割成一横条一横条的,然后对于每一个我们给定的矩形,我们将储存两个点 ...
- hdu-1542 Atlantis(离散化+线段树+扫描线算法)
题目链接: Atlantis Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- 『炸弹 线段树优化建图 Tarjan』
炸弹(SNOI2017) Description 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸 时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi ...
- 洛谷 P6071 『MdOI R1』Treequery(LCA+线段树+主席树)
题目链接 题意:给出一棵树,有边权,\(m\) 次询问,每次给出三个数 \(p,l,r\),求边集 \(\bigcap\limits_{i=l}^rE(p,i)\) 中所有边的权值和. 其中 \(E( ...
- 线段树---Atlantis
题目网址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=110064#problem/A Description There are se ...
- hdu 1542 Atlantis(线段树,扫描线)
Atlantis Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total S ...
随机推荐
- Python学习日记(四十一) Mysql数据库篇 九
前言 索引的主要作用是起到约束和加速查找,ORM框架(sqlalchemy)是用类和对象对数据库进行操作 索引的种类 按种类去分 1.普通索引:能够加速查找 2.主键索引:能够加速查找.不能为空.不能 ...
- 国内不fq安装K8S四: 安装过程中遇到的问题和解决方法
目录 4 安装过程中遇到的问题和解决方法 4.1 常见问题 4.2 常用的操作命令 4.3 比较好的博客 国内不fq安装K8S一: 安装docker 国内不fq安装K8S二: 安装kubernet 国 ...
- dos2unix的使用
由于在DOS(windows系统)下,文本文件的换行符为CRLF,而在Linux下换行符为LF,使用git进行代码管理时,git会自动进行CRLF和LF之间的转换,这个我们不用操心.而有时候,我们需要 ...
- #pragma once用法总结
1.#pragmaonce这个宏有什么作用? 为了避免同一个头文件被包含(include)多次,C/C++中有两种宏实现方式:一种是#ifndef方式:另一种是#pragma once方式. 在能够支 ...
- maven jar包冲突的发现与解决[工具篇]
本文是我的第177篇文章. 关于jar冲突排查解决的问题,相信很多小伙伴也都知道有一些,无非就是两类:命令 or 工具. 命令方式比如: mvn dependency:tree 工具方式比如: Mav ...
- 梅尔倒谱系数特征(Mel-frequency cepstral coefficients,MFCC)
引言 感知实验表明,人耳对于声音信号的感知聚焦于某一特定频率区域内,而非在整个频谱包络中. MFCC特征是应用非常广泛的语音特征. 语音的MFCC特征是基于人耳感知实验得到,将人耳当成特定的滤波器,只 ...
- selenium--页面元素相关的操作
获取元素的标签和元素大小 from selenium import webdriver import unittest class Test_BasicInfo(unittest.TestCase): ...
- c# 文件夹权限
/// <summary> /// 创建文件路径 /// </summary> /// <param name=" ...
- 【cf补题记录】Codeforces Round #608 (Div. 2)
比赛传送门 再次改下写博客的格式,以锻炼自己码字能力 A. Suits 题意:有四种材料,第一套西装需要 \(a\).\(d\) 各一件,卖 \(e\) 块:第二套西装需要 \(b\).\(c\).\ ...
- web前端图片模糊到清晰的实现过程
在网页图片显示的时候,会发现许多网站采用了先模糊,然后在慢慢清晰的过程,这样的加载用户体验是比较好的,那么如何实现? 默认加载2张图片,一张缩略图,一张原图,当打开网页的时候默认只显示缩略图,然后我们 ...