学习扫描线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. POJ1185 状压dp(二进制//三进制)解法

    很显然这是一道状压dp的题目 由于每个最优子结构和前两行有关,一个显而易见的想法是用三维dp[i][j][k]用来记录在第i行下为j状态,i - 1行为k状态时的最大值,然而dp[100][1 < ...

  2. Java_myBatis入门写法

    一.整体步骤 1.读取全局配置文件“SqlMapConfig.xml” 2.用SqlSessionFactoryBuilder来创建一个SqlSessionFactory 3.用创建好的SqlSess ...

  3. MYCAT分库分表

    一.整体架构 1.192.168.189.130:mysql master服务,两个数据库db_store.db_user,db_store做了主从复制 db_user: 用户表users为分片表 数 ...

  4. on条件与where条件的区别

    数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户. 在使用left jion时,on和where条件的区别如下: 1. on条件是在生成临时表时使用的条 ...

  5. 建立SQL链接服务器

    访问链接服务器的格式:select * from [IPMLBZ].[数据库].[dbo].WEB_ItemInfo 有一个最简单的方法就是直接鼠标新建,这样是以ip为开头的,也可以通过下面的代码进行 ...

  6. 跨iOS SDK版本编译问题

    iOS开发时会考虑两种SDK版本兼容性:一个是运行时SDK版本的兼容,即已构建/已发布的APP能在不同系统版本的用户手机上正常运行:一个是编译时SDK版本的兼容,即使用不同版本的SDK编译项目都能正常 ...

  7. CentOS6.x下yum安装MySQL5.5/5.6

    1. 安装mysql-5.5的yum源 # rpm -ivh http://repo.mysql.com/yum/mysql-5.5-community/el/6/x86_64/mysql-commu ...

  8. np.array和np.asarray区别

  9. 选择排序算法的JAVA实现

    1,采用选择排序对元素进行排列时,元素之间需要进行比较,因此需要实现Comparable<T>接口.即,<T extends Comparable<T>>. 更进一 ...

  10. HTML中       等6种空白空格的区别

    HTML提供了5种空格实体(space entity),它们拥有不同的宽度,非断行空格( )是常规空格的宽度,可运行于所有主流浏览器.其他几种空格(       ‌‍)在不同浏览器中宽度各异.   它 ...