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

我们根据容斥原理矩形相互覆盖的面积即为所有矩形的面积和减去所有矩形所覆盖的面积即可。

而现在问题是如何求得所有矩形所覆盖的面积。即

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

此处我们采用割,割成一个一个矩形出来。

这样就很方便地去求了。

计算机无法直观地看出图形,进而去求出长宽进而求出矩形面积。

那该如何让计算机来求?

我们看这些图形,一块一块的,最下面的一块,可以想象成原矩形

下面矩形最下面的边,向上平移时“”出来的紫矩形,当碰到上面矩形的底边时,两条边合并,再向上平移时“”出红色矩形,然后碰到下面矩形的顶边,去掉了顶边对应的底边,因为两边重合的部分有两条边贡献,所以去掉顶边线段后,重合部分还在,继续向上平移,“”出了蓝色矩形。

有序,易行,计算机可做!

其实这就是扫描线在干的事,一条透明的线从下向上平移,遇到底边,则扫描线在底边所在的范围有了颜色,这样向上平移的时候就“”出了颜色,遇到顶边,则扫描线在顶边的范围就没了颜色,之后该部分就没有颜色了。

所以现在的问题是,如何维护扫描线

即我们要维护,扫描线有底边贡献的范围,以及该范围被几条底边所贡献,且当遇到顶边时要把相应底边的贡献去掉

很显然,当底边两端点的坐标为实数,或者坐标范围很大时,直接维护点坐标是不现实的。

实数怎么办?范围很大怎么办?

离散呀!

我们注意到,所有矩形顶点的横坐标,其实是把x轴分成了若干个区间。

而任意一个矩形的底边,只是会覆盖若干个区间,而不会只覆盖某区间的一部分。

那我们就可以维护区间,从而避开了实数的无穷个数和范围大造成数据冗余以及内存爆表的问题。

当我们遇到一个矩形的底边时,只需在底边所覆盖的若干个区间加一,即贡献一个底边。

而在遇到一个矩形的顶边的时候,在顶边所覆盖的若干个区间减一,即去除该矩形底边的贡献。

最终,只要有底边有贡献的区间,都是扫描线“有颜色”的范围。

这里涉及到了区间加,区间减以及区间查询。

拿什么维护呢。

当然是线段树啦。

所以,

根据边的x坐标划分离散x轴的区间,

拿线段树去维护区间,

把边按y轴从小到大排序,自1到n即自下而上扫描,

不断更新扫描线的有颜色的范围长度,再乘以上下两边的y轴的数值差,即为此部分扫描的面积。

重复即可。

离散的写法有好多,这里运用STLl较为简便的写法。

注意离散后也要能映射回去。

POJ1151

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

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

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.
 
大意就是求岛的面积(还是矩形的),给出若干个矩形,以左上和右下点的坐标来表示,矩形间会有重叠,重叠部分只算一次。
扫一下就好了。
注意原题的一句话
Output a blank line after each test case.
否则你会Presentation Error
 
 #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的更多相关文章

  1. 【POJ1151】Atlantis(线段树,扫描线)

    [POJ1151]Atlantis(线段树,扫描线) 题面 Vjudge 题解 学一学扫描线 其实很简单啦 这道题目要求的就是若干矩形的面积和 把扫描线平行于某个轴扫过去(我选的平行\(y\)轴扫) ...

  2. Atlantis poj1151 线段树扫描线

    Atlantis poj1151 线段树扫描线 题意 题目给了n个矩形,每个矩形给了左下角和右上角的坐标,矩形可能会重叠,求的是矩形最后的面积. 题解思路 这个是我线段树扫描线的第一题,听了学长的讲解 ...

  3. POJ1151+线段树+扫描线

    /* 线段树+扫描线+离散化 求多个矩形的面积 */ #include<stdio.h> #include<string.h> #include<stdlib.h> ...

  4. 【POJ1151】【扫描线+线段树】Atlantis

    Description There are several ancient Greek texts that contain descriptions of the fabled island Atl ...

  5. poj1151 Atlantis (线段树+扫描线+离散化)

    有点难,扫描线易懂,离散化然后线段树处理有点不太好理解. 因为这里是一个区间,所有在线段树中更新时,必须是一个长度大于1的区间才是有效的,比如[l,l]这是一根线段,而不是区间了. AC代码 #inc ...

  6. Atlantis(POJ1151+线段树+扫描线)

    题目链接:http://poj.org/problem?id=1151 题目: 题意:求所有矩形的面积,重合部分只算一次. 思路:扫描线入门题,推荐几篇学扫描线的博客: 1.http://www.cn ...

  7. Poj1151&HDU1542 Atlantis(扫描线+线段树)

    题意 给定\(n​\)个矩形\((x_1,y_1,x_2,y_2)​\),求这\(n​\)个矩形的面积并 题解 扫描线裸题,可以不用线段树维护,\(O(n^2)\)是允许的. #include < ...

  8. POJ1151 Atlantis 线段树扫描线

    扫描线终于看懂了...咕咕了快三个月$qwq$ 对于所有的横线按纵坐标排序,矩阵靠下的线权值设为$1$,靠上的线权值设为$-1$,然后执行线段树区间加减,每次的贡献就是有效宽度乘上两次计算时的纵坐标之 ...

  9. ACM学习历程—POJ1151 Atlantis(扫描线 && 线段树)

    Description There are several ancient Greek texts that contain descriptions of the fabled island Atl ...

随机推荐

  1. fileapi.h里的API函数(包括LockFileEx和FindFirstChangeNotification函数)

    /** * This file is part of the mingw-w64 runtime package. * No warranty is given; refer to the file ...

  2. LINQ查询表达式---------orderby子句

    LINQ查询表达式---------orderby子句 LINQ可以按元素的一个或多个属性对元素进行排序. class Program { public class PerInfo { public ...

  3. 微信小程序把玩(十一)icon组件

    原文:微信小程序把玩(十一)icon组件 这些是提供的所支持的图标样式,根据需求在此基础上去修改大小和颜色. 主要属性: 使用方式: wxml <!--成功图标--> <icon t ...

  4. 微信小程序把玩(七)数据绑定

    原文:微信小程序把玩(七)数据绑定 数据绑定有一部分前几个看着还行,后面的几个可能有几个不理解,界面展示的数据有的也因为条件没法显示.看不懂的可以先记着,后面真正用到时就会明白,反正我是这样想的.这里 ...

  5. .NET环境下有关打印页面设置、打印机设置、打印预览对话框的实现

    原文:.NET环境下有关打印页面设置.打印机设置.打印预览对话框的实现 我个人认为,开发MIS,首先就得解决网格的问题,而开发工具为我们提供了如DataGrid.MSHFlexGrid的控件.其次,是 ...

  6. C#数据导出Excel详细介绍

    概要: excel导出在C#代码中应用己经很广泛了,我这里就做些总结,供自己和读者学习用. Excel知识点.一.添加引用和命名空间 添加Microsoft.Office.Interop.Excel引 ...

  7. Attention is all you need及其在TTS中的应用Close to Human Quality TTS with Transformer和BERT

    论文地址:Attention is you need 序列编码 深度学习做NLP的方法,基本都是先将句子分词,然后每个词转化为对应的的词向量序列,每个句子都对应的是一个矩阵\(X=(x_1,x_2,. ...

  8. QML Settings 小的示例

    QML 中使用 Settings 可以保存一些简单的信息,例如用户名,密码,窗口位置,大小等,没有Sqlite那么麻烦,简单易用哦~~~(环境:Qt5.8  for android ,Windows ...

  9. 网络文件系统nfs文件系统使用(比较全面)

    一.NFS简介 1.NFS就是Network FileSystem的缩写,它的最大功能就是可以通过网络让不同的机器,不同的操作系统彼此共享文件(sharefiles)——可以通过NFS挂载远程主机的目 ...

  10. Windows线程生灭(图文并茂)

    一.线程创建 Windows线程在创建时会首先创建一个线程内核对象,它是一个较小的数据结构,操作系统通过它来管理线程.新线程可以访问进程内核对象的所有句柄.进程中的所有内存及同一进程中其它线程的栈. ...