【题目】

Atlantis

Problem 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 file 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.

Sample Input
2 10 10 20 20 15 15 25 25.5 0
Sample Output
Test case #1 Total explored area: 180.00
【题意】
输入n个矩形,求他们总共占地面积(也就是求一下面积的并)
【分析】
ORZ黄学长。。转载一下题解先。。
转自:http://hzwer.com/879.html

第一次做线段树扫描法的题,网搜各种讲解,发现大多数都讲得太过简洁,不是太容易理解。所以自己打算写一个详细的。看完必会o(∩_∩)o

顾名思义,扫描法就是用一根想象中的线扫过所有矩形,在写代码的过程中,这根线很重要。方向的话,可以左右扫,也可以上下扫。方法是一样的,这里我用的是由下向上的扫描法。

如上图所示,坐标系内有两个矩形。位置分别由左下角和右上角顶点的坐标来给出。上下扫描法是对x轴建立线段树,矩形与y平行的两条边是没有用的,在这里直接去掉。如下图。

现想象有一条线从最下面的边开始依次向上扫描。线段树用来维护当前覆盖在x轴上的线段的总长度,初始时总长度为0。用ret来保存矩形面积总和,初始时为0。

由下往上扫描,扫描到矩形的底边时将它插入线段树,扫描到矩形的顶边时将底边从线段树中删除。而在代码中实现的方法就是,每条边都有一个flag变量,底边为1,顶边为-1。

用cover数组(通过线段树维护)来表示某x轴坐标区间内是否有边覆盖,初始时全部为0。插入或删除操作直接让cover[] += flag。当cover[] > 0 时,该区间一定有边覆盖。

开始扫描到第一条线,将它压入线段树,此时覆盖在x轴上的线段的总长度L为10。计算一下它与下一条将被扫描到的边的距离S(即两条线段的纵坐标之差,该例子里此时为3)。

则 ret += L * S. (例子里增量为10*3=30)

结果如下图

  橙色区域表示已经计算出的面积。

扫描到第二条边,将它压入线段树,计算出此时覆盖在x轴上的边的总长度。

例子里此时L=15。与下一条将被扫描到的边的距离S=2。 ret += 30。 如下图所示。

绿色区域为第二次面积的增量。

接下来扫描到了下方矩形的顶边,从线段树中删除该矩形的底边,并计算接下来面积的增量。如下图。

 蓝色区域为面积的增量。

此时矩形覆盖的总面积已经计算完成。 可以看到,当共有n条底边和顶边时,只需要从下往上扫描n-1条边即可计算出总面积。

此题因为横坐标包含浮点数,因此先离散化。另外,因为用线段树维护的是覆盖在x轴上的边,而边是连续的,并非是一个个断点,因此线段树的每一个叶子结点实际存储的是该点与下一点之间的距离。

12.25

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<cmath>
  7 using namespace std;
  8 #define Maxn 100010
  9
 10 struct hp
 11 {
 12     int x1,x2,flag;
 13     double y;
 14 }t[Maxn*2];int tl;
 15 struct lsh
 16 {
 17     double x;
 18     int id;
 19 }q[Maxn*2];int ql;
 20
 21 bool cmp(lsh x,lsh y) {return x.x<y.x;}
 22 bool cmp2(hp x,hp y) {return x.y<y.y;}
 23
 24 double dis[2*Maxn];
 25
 26 struct node
 27 {
 28     int l,r,lc,rc,cnt;
 29     double sm;
 30 }tr[Maxn*2];int len;
 31 int build(int l,int r)
 32 {
 33     int x=++len;
 34     tr[x].l=l;tr[x].r=r;
 35     tr[x].sm=tr[x].cnt=0;
 36     if(l<r-1)
 37     {
 38         int mid=(tr[x].l+tr[x].r)>>1;
 39         tr[x].lc=build(l,mid);
 40         tr[x].rc=build(mid,r);
 41     }
 42     else tr[x].lc=tr[x].rc=0;
 43     return x;
 44 }
 45
 46 void upd(int x)
 47 {
 48     int l=tr[x].l,r=tr[x].r;
 49     if(tr[x].cnt!=0) tr[x].sm=dis[r]-dis[l];
 50     else tr[x].sm=tr[tr[x].lc].sm+tr[tr[x].rc].sm;
 51 }
 52
 53 void change(int x,int l,int r,int c)
 54 {
 55     if(tr[x].l==l&&tr[x].r==r)
 56     {
 57         tr[x].cnt+=c;
 58         upd(x);
 59         return;
 60     }
 61     int mid=(tr[x].l+tr[x].r)>>1;
 62     if(r<=mid) change(tr[x].lc,l,r,c);
 63     else if(l>=mid) change(tr[x].rc,l,r,c);
 64     else
 65     {
 66         change(tr[x].lc,l,mid,c);
 67         change(tr[x].rc,mid,r,c);
 68     }
 69     upd(x);
 70 }
 71
 72 int main()
 73 {
 74     int n,kase=0;
 75     while(1)
 76     {
 77         scanf("%d",&n);
 78         if(n==0) break;
 79         tl=0;ql=0;
 80         for(int i=1;i<=n;i++)
 81         {
 82             double x1,y1,x2,y2;
 83             scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
 84             //t[++tl].x1,
 85             t[++tl].flag=1;t[tl].y=y1;
 86             t[++tl].flag=-1;t[tl].y=y2;
 87             q[++ql].x=x1,q[ql].id=tl;
 88             q[++ql].x=x2,q[ql].id=tl+2*n;
 89         }
 90         sort(q+1,q+1+ql,cmp);
 91         int p=0;
 92         for(int i=1;i<=ql;i++)
 93         {
 94             if(q[i].x!=q[i-1].x||p==0) p++,dis[p]=dis[p-1]+q[i].x-q[i-1].x;
 95             if(q[i].id<=2*n) t[q[i].id].x1=t[q[i].id-1].x1=p;
 96             else t[q[i].id-2*n].x2=t[q[i].id-1-2*n].x2=p;
 97         }
 98         sort(t+1,t+1+tl,cmp2);
 99         len=0;tr[0].sm=0;
100         build(1,p);
101         double ans=0;
102         for(int i=1;i<tl;i++)
103         {
104             change(1,t[i].x1,t[i].x2,t[i].flag);
105             ans+=tr[1].sm*(t[i+1].y-t[i].y);
106         }
107         printf("Test case #%d\nTotal explored area: %.2lf\n\n",++kase,ans);
108     }
109     return 0;
110 }

View Code



自己总结:
一开始觉得是转化成
给定区间[l,r] 然后让你+1 -1 ,然后不会减成负数,问你里面有多少个不为0的数。
ORZ。。不会做。。不会用线段树维护。。。
其实这题还有个特点,他是矩形。。。
就是说 不会给你[1,2] [2,3] +1 [1,3]-1 然后询问
就是说如果[l,r]-1,那么前面一定有一个[l,r]+1的操作。。。
所以,思想有点像打lazy标记,这里如果询问到整个区间[l,r],那么就直接在代表这个区间的点上面cnt+1。。。
要保证的是每个点的sm值都是正确的。(sm表示这段区间的覆盖长度)
update的话,就是 若cnt>0 sm就是区间长度,否则是孩子区间长度的和。(保证每个点的sm都是对的,那么要回溯upd)
还有就是这棵线段树表示连续区间,所以是[l,mid][mid,r]而不是[l,mid][mid+1,r]..
接下来ORZ->gdxb,他的最后一句话挺有意思:

这道题我一直在纠结,怎么求当前有扫描线上有的线段总长?怎么lazy下放?我一直想的是每个点维护的都是它维护的这个区间内的总的cnt等等。

后来我发现换个思路,一切都很简单!

我的每个节点t[x].l~t[x].r维护的其实是线段t[x].l~(t[x].r+1),也就是若干条线段,因为点分成左右孩子的时候会有问题(比如[3,3]维护的到底是什么?)。

我们要把每个节点看成是一条线段。

对于每个节点维护两个值:

cnt:这个点所代表的线段被覆盖了多少次。

len:以这个点为根的子树中被覆盖的区间一共有多长。

当一条线段进来的时候,在代表它的那若干个节点上cnt++,其它节点cnt不用加。

然后len维护的就是这个区间内那些cnt>0的节点所覆盖的区间总长。

我做惯了叶子节点才有实际意义的线段树,思路太过狭隘,被卡了这么久,其实线段树上每个节点都可以有它的实际意义。

http://www.cnblogs.com/KonjakJuruo/p/6024266.html

2016-11-10 11:46:16

转载于:https://www.cnblogs.com/Konjakmoyu/p/6050343.html

【HDU 1542】Atlantis 矩形面积并(线段树,扫描法)相关推荐

  1. HDU - 1255 覆盖的面积(线段树求矩形面积交 扫描线+离散化)

    链接:线段树求矩形面积并 扫描线+离散化 1.给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. 2.看完线段树求矩形面积并 的方法后,再看这题,求的是矩形面积交,类同. 求面积时,用被覆 ...

  2. HDU 1255 覆盖的面积(线段树+扫描线)

    题目地址:HDU 1255 这题跟面积并的方法非常像,仅仅只是须要再加一个变量. 刚開始我以为直接用那个变量即可,仅仅只是推断是否大于0改成推断是否大于1.可是后来发现了个问题,由于这个没有下放,没延 ...

  3. hdu 1542 Atlantis (线段树+扫描线)

    http://acm.hdu.edu.cn/showproblem.php?pid=1542 单纯的线段树+扫描线求面积并,需要离散化. code: #include <cstdlib> ...

  4. POJ 1151 Atlantis 矩形面积求交/线段树扫描线

    Atlantis 题目连接 http://poj.org/problem?id=1151 Description here are several ancient Greek texts that c ...

  5. HDU 1542 Atlantis 线段树+离散化+扫描线

    题意:给出一些矩形的最上角坐标和右下角坐标,求这些矩形的面积并. NotOnlySuccess 线段树专辑中扫描线模板题,弱智的我对着大大的代码看了一下午才搞懂. 具体见思路见注释=.= #inclu ...

  6. HDU - 1542 Atlantis(线段树+扫描线)

    题目链接:点击查看 题目大意:给出n个矩形的左下角坐标和右上角坐标,求出其总面积,注意,矩形会重叠,重叠部分只计算一次 题目分析:如果暴力做需要考虑容斥原理,两两矩形组合判断是否重合,十分麻烦而且时间 ...

  7. HDU 1394 Minimum Inversion Number(线段树的单点更新)

    点我看题目 题意 :给你一个数列,a1,a2,a3,a4.......an,然后可以求出逆序数,再把a1放到an后,可以得到一个新的逆序数,再把a2放到a1后边,,,,,,,依次下去,输出最小的那个逆 ...

  8. hdu 5493 Queue(逆序对,线段树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5493 解题思路: 一道线段树的题目,因为是前面或者后面有k个比自己高的人,所以我们应该按照由身高从小到 ...

  9. [矩形并-扫描线-线段树]Picture

    最近在补数学和几何,没啥好写的,因为已经决定每天至少写一篇了,今天随便拿个题水水. 题目大意:给你N个边平行于坐标轴的矩形,求它们并的周长.(N<=5000) 思路:这个数据范围瞎暴力就过了,但 ...

最新文章

  1. albian开发笔记五--谈缓存同步
  2. Android数据存储之SQLite
  3. 算法入门篇六 二叉树
  4. linux netcat测试udp端口,使用nc(netcat)测试udp协议与端口连通性
  5. ccs10怎么导入工程文件_FCPX导入位置选项时如何使用!
  6. Python编程各种推导式详解
  7. python + opencv: 解决不能读取视频的问题
  8. 自定义 Java Annotation ,读取注解值
  9. TCP连接建立与释放
  10. CAD编辑指南2:五大CAD查看器的功能盘点
  11. java商城答辩_java网上商城系统毕业设计答辩.ppt
  12. Hadoop常用端口号汇总
  13. MES生产管理系统源码 生产执行系统源码
  14. hiberfil.sys文件过大
  15. python自动化办公之爬取HTML图片写入PPT实战
  16. .Net Core怎么使用Hangfire
  17. 一文看懂为什么边缘计算是大势所趋
  18. STM32的SG90舵机驱动
  19. 花花野公子 - 野行之~昆明大理
  20. oracle系统计算工资,阿里巴巴工资怎么算?自研薪酬管理系统首次曝光

热门文章

  1. php的div和p的区别,p标签与div标签区别
  2. oracle数据磊导入数据,可传输表空间记载
  3. mysql命令行各个参数解释
  4. 2018,开工第一天
  5. 字节跳动最新开源!java界面实现查询功能
  6. nginx负载均衡策略upstream
  7. 别再说自己不会了!最新高频Java笔试题分享
  8. 常见OJ评判结果对照表
  9. linux文件属性 -rwxr-xrw,Linux文件属性
  10. 缺少nst linux.mbr文件,用EasyBCD2.0在Windows环境下引导Linux启动