扫描线——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 ...
随机推荐
- ReportViewer,RDLC 报表开发之个性化样式
原文:ReportViewer,RDLC 报表开发之个性化样式 报表开发中,客户对样式提出了要求: 1.工具栏上显示每页条数 2.只导出Excel,不需要下拉菜单. 3.报表上显示的图表,分页时,每页 ...
- [Leetcode]Single Number && Single Number II
Given an array of integers, every element appears twice except for one. Find that single one. 非常简单的一 ...
- Firemonkey实现Mac OS程序中内嵌浏览器的功能(自己动手翻译,调用苹果提供的webkit框架)
XE系列虽然可以跨平台,但是在跨平台的道路上只是走了一小半的路,很多平台下的接口都没实现彻底,所以为了某些功能,还必须自己去摸索. 想实现程序中可以内嵌浏览器的功能,但是Firemonkey还没有对应 ...
- 记一次排查tomcat耗费CPU过高的经历
有一个新项目,在测试环境部署后,发现tomcat进程耗费的CPU非常高,排查过程如下: 日志搜集 先通过top,查找耗费CPU最高的线程 top -Hp pid 将线程ID转为16进制 printf ...
- C++ Builder 控件的卸载
控件卸载: 1.选择 BCB 菜单 File→Close All (关闭所有文件) 选择BCB 菜单: Project→Options→Packages 在 ...
- Webapi实现websocket实时通讯
应用场景:前端页面发起一个websocket请求与后端进行实时通讯.后端监听某端口获取数据,将监听到的数据加工处理,通过websocket发送到前端. 这里只提供后台的处理方案仅供参考. 1.后端监听 ...
- spark streaming 接收kafka消息之一 -- 两种接收方式
源码分析的spark版本是1.6. 首先,先看一下 org.apache.spark.streaming.dstream.InputDStream 的 类说明: This is the abstrac ...
- devexpress 给GridView添加行号
先找到 此时间gridView1_CustomDrawRowIndicator private void gridView1_CustomDrawRowIndicator(object sender, ...
- Netty源码分析--Channel注册(上)(五)
其实在将这一节之前,我们来分析一个东西,方便下面的工作好开展. 打开启动类,最开始的时候创建了一个NioEventLoopGroup 事件循环组,我们来跟一下这个. 这里bossGroup, 我传入了 ...
- Android 即时通讯开发小结(二)
<Android 即时通讯开发小结>基于IM Andriod 开发的各种常见问题,结合网易云信即时通讯技术的实践,对IM 开发做一个全面的总结. 相关推荐阅读:. Android 即时通讯 ...