扫描线——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 ...
随机推荐
- delphi读取ini文件
ini文件在系统配置及应用程序参数保存与设置方面,具有很重要的作用,所以可视化的编程一族,如vb.vc.vfp.delphi等都提供了读写ini文件的方法,其中delphi中操作ini文件,最为简洁, ...
- Android零基础入门第73节:Activity初入门,创建和配置如此简单
Activity是Android应用的重要组成单元之一,也是Android应用最常见的组件之一.前面看到的示例通常都只包含一个Activity或一个AppCompatActivity,但在实际应用中这 ...
- Twitter的分布式自增ID算法snowflake(雪花算法) - C#版
概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的.有些时候我们希望能使用一种简 ...
- JavaScript语言核心--词法结构
编程语言的词法结构是一套基础性规则,用来描述如何使用这门语言来编写程序.作为语法的基础,它规定了诸如变量名是什么样的.怎么写注释,以及程序语言之间如何分隔等规则. 1. 字符集 JavaScript程 ...
- Homebrew 1.0.0 发布,MacOS 上的包管理器,比如安装qt5keychain
神器,没有它不知道怎么用macos https://www.oschina.net/news/77367/homebrew-1-0-0 Mac OS X用户,qt5keychain可以使用homebr ...
- delphi 读写文本文件(函数比较全)
需要两个按钮和两个Richedit控件,采用默认名称即可. procedure TForm1.Button1Click(Sender: TObject); //写文件 var wText: Text ...
- <房间内功能>打赏小动画
截图如下 : 功能: 每次点击礼物,都要通过动画显示一个小图标,最多显示两行图标栏,送多次会显示然后再次显示,显示 XX 送给 XX 一个小礼物的动画样式.动画样式效果为,整体动画模 ...
- 事务 ( 进程 ID 60) 与另一个进程被死锁在锁资源上,并且已被选作死锁牺牲品
Select * FROM [TableName] With(NoLock) .....
- 在无界面centos7上部署jdk和tomcat
1.安装xshell6 2.创建服务器连接,输入用户名和密码 3.输入 sudo su -root 获取root权限 4.输入 cd /usr/local 进入local文件夹 5.输入 wget - ...
- PHP实现图片(文件)上传
这几天整理做过的php项目,感觉这个经常会用到,传上来共享一下咯 首先,前端界面 1.表单的首行需要加上enctype="multipart/form-data",需要上传的图片必 ...