学习扫描线ing...

玄学的东西...

扫描线其实就是用一条假想的线去扫描一堆矩形,借以求出他们的面积或周长(这一篇是面积,下一篇是周长)

扫描线求面积的主要思想就是对一个二维的矩形的某一维上建立一棵线段树,然后把另一维按高度排序,从下向上枚举即可。

主题思想其他博客说的很明白了,这里重点记录一下细节问题:

下面认为对横坐标建立线段树扫描纵坐标:

首先,由于读入的都是浮点数,所以我们需要对这个东西离散化,具体做法是先去重再进行二分查找,以下标代替浮点数值。

其次,由于普通线段树维护的是一个散点区间,而这里我们需要维护一整个连续的区间,所以区间的端点就是有说道的。具体来讲,我们采用一个“左闭右开”的区间,如何维护?

int lc=findf(p[i].lp);
int rc=findf(p[i].rp)-1;

如上代码所示,我们在查找左端点时是正常查找下标,而查找右端点时我们把查找出来的下标-1,这是为什么呢?

因为我们在线段树上的查询(同时修改)是这样进行的:

void change(int rt,int l,int r)
{
if(tree[rt].lazy)
{
tree[rt].sum=x[r+1]-x[l];
}else if(l==r)
{
tree[rt].sum=0;
}else
{
tree[rt].sum=tree[rt1].sum+tree[rt2].sum;
}
}
void ins(int rt,int l,int r,int v)
{
if(ls>=l&&rs<=r)
{
tree[rt].lazy+=v;
change(rt,ls,rs);
return;
}
int mid=(ls+rs)>>1;
if(l<=mid)
{
ins(rt1,l,r,v);
}
if(r>mid)
{
ins(rt2,l,r,v);
}
change(rt,ls,rs);
}

如上,我们举个例子:

(图片粘不上来)

简而言之,就是如果我们有某种状况:矩形覆盖了区间[3,5],那么如果用普通的线段树,我们就会计算区间[3,4]和...[5]?

这样显然是不可以的

所以如上,我们在查询更新的时候,我们把这个区间外的一个点累计进这个区间里,即我们令区间[3,4]累计的是区间[3,5)(左闭右开)的和,这样就得到了优化。

还有一个问题,就是如果我们这样查询,万一有一个区间叫[5,6],按这样操作就变成了查询[5,7),这样显然是错误的。

所以我们在查询的时候,故意把右端点坐标-1,如果想查询区间[5,6],我们把他变成查询区间[5],然后在查询区间[5]的时候根据上述操作去查询区间[5,6),这样就能获得最好的效果了。

贴代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define rt1 rt<<1
#define rt2 (rt<<1)|1
#define ls tree[rt].lson
#define rs tree[rt].rson
using namespace std;
struct Tree
{
int lson;
int rson;
int lazy;
double sum;
}tree[800005];
int n,cnt;
struct node
{
double lp,rp,hei;
int typ;
}p[200005];
double x[200005];
bool cmp(node a,node b)
{
return a.hei<b.hei;
}
bool cmp1(double a, double b)
{
return a<b;
}
void buildtree(int rt,int l,int r)
{
ls=l;
rs=r;
tree[rt].lazy=0;
tree[rt].sum=0;
if(l==r)
{
return;
}
int mid=(l+r)>>1;
buildtree(rt1,l,mid);
buildtree(rt2,mid+1,r);
}
int findf(double val)
{
int l=1,r=cnt;
while(l<=r)
{
int mid=(l+r)>>1;
if(x[mid]==val)
{
return mid;
}else if(x[mid]>val)
{
r=mid-1;
}else
{
l=mid+1;
}
}
return -1;
}
void change(int rt,int l,int r)
{
if(tree[rt].lazy)
{
tree[rt].sum=x[r]-x[l];
}else if(l==r)
{
tree[rt].sum=0;
}else
{
tree[rt].sum=tree[rt1].sum+tree[rt2].sum;
}
}
void ins(int rt,int l,int r,int v)
{
if(ls>=l&&rs<=r)
{
tree[rt].lazy+=v;
change(rt,ls,rs);
return;
}
int mid=(ls+rs)>>1;
if(l<=mid)
{
ins(rt1,l,r,v);
}
if(r>mid)
{
ins(rt2,l,r,v);
}
change(rt,ls,rs);
}
int main()
{
int cas=1;
while(1)
{
scanf("%d",&n);
if(n==0)
{
break;
}
int tot=0;
for(int i=1;i<=n;i++)
{
double a,b,c,d;//x1,y1,x2,y2,左上角和右下角
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
x[++tot]=a;
p[tot].lp=a;
p[tot].rp=c;
p[tot].typ=1;
p[tot].hei=b;
x[++tot]=c;
p[tot].lp=a;
p[tot].rp=c;
p[tot].hei=d;
p[tot].typ=-1;
}
sort(x+1,x+tot+1,cmp1);
sort(p+1,p+tot+1,cmp);
cnt=0;
for(int i=2;i<=tot+1;i++)
{
if(x[i]!=x[i-1])
{
x[++cnt]=x[i-1];
}
}
buildtree(1,1,cnt);
double ret=0;
for(int i=1;i<tot;i++)
{
int lc=findf(p[i].lp);
int rc=findf(p[i].rp);
if(lc<=rc)
{
ins(1,lc,rc,p[i].typ);
}
ret+=tree[1].sum*(p[i+1].hei-p[i].hei);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++,ret);
}
return 0;
}

hdu 1542 线段树+扫描线 学习的更多相关文章

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

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  2. Atlantis HDU - 1542 线段树+扫描线 求交叉图形面积

    //永远只考虑根节点的信息,说明在query时不会调用pushdown //所有操作均是成对出现,且先加后减 // #include <cstdio> #include <cstri ...

  3. hdu 4052 线段树扫描线、奇特处理

    Adding New Machine Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...

  4. hdu 1542 线段树扫描(面积)

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  5. hdu 1828 线段树扫描线(周长)

    Picture Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  6. hdu 5091(线段树+扫描线)

    上海邀请赛的一道题目,看比赛时很多队伍水过去了,当时还想了好久却没有发现这题有什么水题的性质,原来是道成题. 最近学习了下线段树扫描线才发现确实是挺水的一道题. hdu5091 #include &l ...

  7. HDU 5107 线段树扫描线

    给出N个点(x,y).每一个点有一个高度h 给出M次询问.问在(x,y)范围内第k小的高度是多少,没有输出-1 (k<=10) 线段树扫描线 首先离散化Y坐标,以Y坐标建立线段树 对全部的点和询 ...

  8. hdu 1542 线段树之扫描线之面积并

    点击打开链接 题意:给你n个矩形,求它们的面积,反复的不反复计算 思路:用线段树的扫描线完毕.将X坐标离散化后,从下到上扫描矩形,进行各种处理,看代码凝视把 #include <stdio.h& ...

  9. hdu 1255(线段树 扫描线) 覆盖的面积

    http://acm.hdu.edu.cn/showproblem.php?pid=1255 典型线段树辅助扫描线,顾名思义扫描线就是相当于yy出一条直线从左到右(也可以从上到下)扫描过去,此时先将所 ...

随机推荐

  1. python自动化运维之路~DAY7

    python自动化运维之路~DAY7 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.客户端/服务器架构 C/S 架构是一种典型的两层架构,其全称是Client/Server,即 ...

  2. db nosql redis / Redis Sentinel

    s Redis基础原理和日常操作方法 http://itsm.cns*****.com/kindeditor/img/20170527/759128afca564051b491e6a51a5bad40 ...

  3. 函数和常用模块【day05】:生成器(四)

    本节内容 1.概述 2.列表生成式 3.生成器 4.函数实现生成器 5.生成器表达式 一.概述 我们在使用一组数据时,通常情况下会定义一个列表,然后循环里面的元素,但是你想过没有,如果你只需要使用列表 ...

  4. window下nginx负载均衡简单配置-----权重的实现

    下面介绍一个在window下的nginx的负载均衡配置. 需要你在你的电脑上跑两个tomcat.一个8080,一个9080. 需要一个nginx服务器. 需要修改本机的host 注意:我们这里配置不会 ...

  5. Spark记录-Spark性能优化(开发、资源、数据、shuffle)

    开发调优篇 原则一:避免创建重复的RDD 通常来说,我们在开发一个Spark作业时,首先是基于某个数据源(比如Hive表或HDFS文件)创建一个初始的RDD:接着对这个RDD执行某个算子操作,然后得到 ...

  6. Spark记录-Spark-Shell客户端操作读取Hive数据

    1.拷贝hive-site.xml到spark/conf下,拷贝mysql-connector-java-xxx-bin.jar到hive/lib下 2.开启hive元数据服务:hive  --ser ...

  7. python学习笔记9--日志模块logging

    我们在写程序的时候经常会打一些日志来帮助我们查找问题,这次学习一下logging模块,在python里面如何操作日志.介绍一下logging模块,logging模块就是python里面用来操作日志的模 ...

  8. 20155228 2016-2017-2 《Java程序设计》第7周学习总结

    20155228 2016-2017-2 <Java程序设计>第7周学习总结 教材学习内容总结 Lambda 方法参考的特性,在重用现有的API上扮演了重要的角色.重用现有方法操作,可以避 ...

  9. android SQLiteOpenHelper 使用

    1.实体 package mydemo.mycom.demo2.entity; public class UserInfo { private int id; private String usern ...

  10. JavaScript之小工具之日志log()[兼容]

    function log(){ try{ console.log.apply(console,arguments); }catch(e){ try{ opera.postError.apply(ope ...