扫描线——POJ1151
平面上有若干个矩形,求矩形相互覆盖的面积。为方便起见,矩形的边均平行于坐标轴。

我们根据容斥原理,矩形相互覆盖的面积即为所有矩形的面积和减去所有矩形所覆盖的面积即可。
而现在问题是如何求得所有矩形所覆盖的面积。即

让我们人类去做,由于这是个由矩形拼接成的多边形,很难去直接求它的面积,求该图形的面积一个常规的方法就是割补法。
此处我们采用割,割成一个一个矩形出来。

这样就很方便地去求了。
计算机无法直观地看出图形,进而去求出长宽进而求出矩形面积。
那该如何让计算机来求?
我们看这些图形,一块一块的,最下面的一块,可以想象成原矩形

下面矩形最下面的边,向上平移时“刷”出来的紫矩形,当碰到上面矩形的底边时,两条边合并,再向上平移时“刷”出红色矩形,然后碰到下面矩形的顶边,去掉了顶边对应的底边,因为两边重合的部分有两条边贡献,所以去掉顶边线段后,重合部分还在,继续向上平移,“刷”出了蓝色矩形。
有序,易行,计算机可做!
其实这就是扫描线在干的事,一条透明的线从下向上平移,遇到底边,则扫描线在底边所在的范围有了颜色,这样向上平移的时候就“刷”出了颜色,遇到顶边,则扫描线在顶边的范围就没了颜色,之后该部分就没有颜色了。
所以现在的问题是,如何维护扫描线。
即我们要维护,扫描线有底边贡献的范围,以及该范围被几条底边所贡献,且当遇到顶边时要把相应底边的贡献去掉。
很显然,当底边两端点的坐标为实数,或者坐标范围很大时,直接维护点坐标是不现实的。
实数怎么办?范围很大怎么办?
离散呀!
我们注意到,所有矩形顶点的横坐标,其实是把x轴分成了若干个区间。
而任意一个矩形的底边,只是会覆盖若干个区间,而不会只覆盖某区间的一部分。
那我们就可以维护区间,从而避开了实数的无穷个数和范围大造成数据冗余以及内存爆表的问题。
当我们遇到一个矩形的底边时,只需在底边所覆盖的若干个区间加一,即贡献一个底边。
而在遇到一个矩形的顶边的时候,在顶边所覆盖的若干个区间减一,即去除该矩形底边的贡献。
最终,只要有底边有贡献的区间,都是扫描线“有颜色”的范围。
这里涉及到了区间加,区间减以及区间查询。
拿什么维护呢。
当然是线段树啦。
所以,
根据边的x坐标划分离散x轴的区间,
拿线段树去维护区间,
把边按y轴从小到大排序,自1到n即自下而上扫描,
不断更新扫描线的有颜色的范围长度,再乘以上下两边的y轴的数值差,即为此部分扫描的面积。
重复即可。
离散的写法有好多,这里运用STLl较为简便的写法。
注意离散后也要能映射回去。
Description
Input
The input file is terminated by a line containing a single 0. Don't process it.
Output
Output a blank line after each test case.
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <cmath>
#include <ctime>
#include <queue>
#define N 200
using namespace std;
struct seg{ //边
double l,r,h;
int d;
seg(){}
seg(double xx,double yy,double hh,int dd):l(xx),r(yy),h(hh),d(dd){} //初始化函数
bool operator < (const seg &a) const{ //重载运算符,使其能够进行快排
return (h<a.h);
}
}s[N*];
struct tree{ //线段树
int mark; //mark记录底边对区间的贡献
double sum; //sum记录底边贡献的范围
}t[N*];
int n,ll,rr,num,size,qwq; //qwq
double ans,rank[N*]; //rank做离散用
void readint(int &x){
x=;
char c;
int w=;
for (c=getchar();c<''||c>'';c=getchar())
if (c=='-') w=-;
for (;c>=''&&c<='';c=getchar())
x=(x<<)+(x<<)+(c^'');
x*=w;
}
void readlong(long long &x){
x=;
char c;
long long w=;
for (c=getchar();c<''||c>'';c=getchar())
if (c=='-') w=-;
for (;c>=''&&c<='';c=getchar())
x=(x<<)+(x<<)+(c^'');
x*=w;
}
void pushup(int root,int ll,int rr){
if (t[root].mark) t[root].sum=rank[rr+]-rank[ll]; //离散后的数字映射回原来的数字
else if (ll==rr) t[root].sum=;
else t[root].sum=t[root<<].sum+t[root<<|].sum;
}
void updata(int l,int r,int d,int root,int ll,int rr){
if (l<=ll&&rr<=r){
t[root].mark+=d;
pushup(root,ll,rr);
return;
}
int mid=(ll+rr)>>;
if (l<=mid) updata(l,r,d,root<<,ll,mid);
if (r>mid) updata(l,r,d,root<<|,mid+,rr);
pushup(root,ll,rr);
}
int main(){
qwq=;
while (true){
++qwq; //qwq只是情况记录的个数qwq
num=;
ans=;
readint(n);
if (n==) return ; //应题目要求
for (int i=;i<=n;++i){
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
s[++num]=seg(x1,x2,y2,);
rank[num]=x1;
s[++num]=seg(x1,x2,y1,-);
rank[num]=x2;
}
sort(rank+,rank++num);
sort(s+,s++num);
size=unique(rank+,rank++num)-(rank+); //去重,size为去重后点的个数,为离散做准备
s[num+].h=s[num].h; //小细节,只为下面i循环后mark能清零且不会在i=n时对结果造成影响
for (int i=;i<=num;++i){
ll=lower_bound(rank+,rank++size,s[i].l)-(rank+)+; //这里左边第一个点标号为1,其点右边第一个区间标号也为1
rr=lower_bound(rank+,rank++size,s[i].r)-(rank+); //离散,ll,rr为边覆盖的最左区间和最右区间的标号
updata(ll,rr,s[i].d,,,size-); //植树原理,一条直线,两头植树,n个点,n-1个区间
ans+=t[].sum*(s[i+].h-s[i].h); //长乘宽
}
printf("Test case #%d\nTotal explored area: %.2f\n\n",qwq,ans);
}
return ;
}
神奇的代码
什么?你说4+2第一天那道绝地求生,有若干个圆形辐射区,问安全区的面积怎么用扫描线扫?
那听说是道圆的面积并的模板题,但本蒟蒻还不会……
扫描线——POJ1151的更多相关文章
- 【POJ1151】Atlantis(线段树,扫描线)
[POJ1151]Atlantis(线段树,扫描线) 题面 Vjudge 题解 学一学扫描线 其实很简单啦 这道题目要求的就是若干矩形的面积和 把扫描线平行于某个轴扫过去(我选的平行\(y\)轴扫) ...
- Atlantis poj1151 线段树扫描线
Atlantis poj1151 线段树扫描线 题意 题目给了n个矩形,每个矩形给了左下角和右上角的坐标,矩形可能会重叠,求的是矩形最后的面积. 题解思路 这个是我线段树扫描线的第一题,听了学长的讲解 ...
- POJ1151+线段树+扫描线
/* 线段树+扫描线+离散化 求多个矩形的面积 */ #include<stdio.h> #include<string.h> #include<stdlib.h> ...
- 【POJ1151】【扫描线+线段树】Atlantis
Description There are several ancient Greek texts that contain descriptions of the fabled island Atl ...
- poj1151 Atlantis (线段树+扫描线+离散化)
有点难,扫描线易懂,离散化然后线段树处理有点不太好理解. 因为这里是一个区间,所有在线段树中更新时,必须是一个长度大于1的区间才是有效的,比如[l,l]这是一根线段,而不是区间了. AC代码 #inc ...
- Atlantis(POJ1151+线段树+扫描线)
题目链接:http://poj.org/problem?id=1151 题目: 题意:求所有矩形的面积,重合部分只算一次. 思路:扫描线入门题,推荐几篇学扫描线的博客: 1.http://www.cn ...
- Poj1151&HDU1542 Atlantis(扫描线+线段树)
题意 给定\(n\)个矩形\((x_1,y_1,x_2,y_2)\),求这\(n\)个矩形的面积并 题解 扫描线裸题,可以不用线段树维护,\(O(n^2)\)是允许的. #include < ...
- POJ1151 Atlantis 线段树扫描线
扫描线终于看懂了...咕咕了快三个月$qwq$ 对于所有的横线按纵坐标排序,矩阵靠下的线权值设为$1$,靠上的线权值设为$-1$,然后执行线段树区间加减,每次的贡献就是有效宽度乘上两次计算时的纵坐标之 ...
- ACM学习历程—POJ1151 Atlantis(扫描线 && 线段树)
Description There are several ancient Greek texts that contain descriptions of the fabled island Atl ...
随机推荐
- Win8 Metro(C#)数字图像处理--2.60部分彩色保留算法
原文:Win8 Metro(C#)数字图像处理--2.60部分彩色保留算法 [函数名称] 部分彩色保留函数 WriteableBitmap PartialcolorProcess ...
- Windows 10开发基础——文件、文件夹和库(二)
主要内容: 使用选取器打开和保存文件 关于文件.文件夹和库,如果深究其实还是有比较多的内容,我们这一次来学习一下选取器就收了.还有上篇博文中读写文本文件的三种方式可以细细体会一下. 文件选取器包含文件 ...
- UWP入门(一) -- 先写几个简单控件简单熟悉下(别看这个)
原文:UWP入门(一) -- 先写几个简单控件简单熟悉下(别看这个) 1. MainPage.xmal <Grid Background="{ThemeResource Applica ...
- 流程图浅析MFC架构
http://blog.csdn.net/qq2399431200/article/details/9035315
- Delphi线程类 DIY(把类指针作为参数传进去,就可以执行类里面的方法啦)
Delphi 封装了一个很强大的线程类 TThread, 我们也自己动手制作一个简单的线程类 首先Type一个类 type TwwThread = class constructor Create; ...
- linux下mysql定时备份
1. 在服务器上建立备份文件的存放文件夹 sudo mkdir /usr/local/dbbackup 2. 编写备份脚本 vi dbbackup.sh 在里面编写如下内容 mysqldump -ur ...
- 为QNetworkAccessManager添加超时提醒(自己记录一段时间里的下载字节数,用定时器去定期检测,从而判断是否超时)
在做更新,在测试异常的时候,在下载过程中,发现如果直接系统禁用了网络,会报错误,可以捕获.但是如果是第三方软件限制程序联网,问题来了. 程序会一直在那里等待,没有异常,也不发送QNetworkAcce ...
- spring boot之actuator简介
当我们的开发工作进入尾声,部署上线之后,对于一个程序而言,可能才刚刚开始,对程序的运行情况的监控要伴随着整个生命周期. 如果这个工作由程序员自己来开发,也未尝不可,但本着不重复制造轮子的思想,我们尽量 ...
- python常用数据结构(1)
python中有四种最常用的数据结构,分别是列表(list),字典(dict),集合(set)和元组(tuple) 下面简单描述下它们的区别和联系 1.初始化 不得不说,python数据结构的初始化比 ...
- 为了考PMP,我做了一个刷题小程序
一.背景 1.我是一名软件工程师,技术出身,担任开发组长,对项目管理不是很熟,所以决定系统学习下项目管理. 2.全球最适合的项目管理学习课程就是PMP,每年有4次PMP考试,证书还是很有含金量的. 3 ...