学习扫描线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. passat / maintenance / baoyang

    s s 南京迎客隆汽车租赁有限公司 / 地址:常府街54号 / 电话:025-84546836 84507610 二手车养不起.修不起?果真如此吗?http://www.che168.com/list ...

  2. 客户端连接linux经常间隔性断开链接【转】

    起因 在使用SecureCRT通过telnet或SSH访问linux时,总是出现过段时间操作就会断开连接提示重连的问题.起初以为是网络不稳定造成的,但我测试发现在服务器端一直可以ping通客户端IP, ...

  3. GsonWithoutObject 没有对象(脱离对象) 直接提取【转】

    GsonWithoutObject 没有对象(脱离对象) 直接提取 ... gson json GsonWithoutObject 脱离对象, 直接提取 package temp; import to ...

  4. ThinkPHP 3.2公共类库、应用类库ThinkPHP/Library讲解

    一.ThinkPHP的类库主要包括公共类库和应用类库,都是基于命名空间进行定义和扩展的.只要按照规范定义,都可以实现自动加载.        公共类库 公共类库通常是指ThinkPHP/Library ...

  5. MIPS架构上函数调用过程的堆栈和栈帧

    转载于CSDN:http://blog.csdn.net/do2jiang/article/details/5404566 在计算机科学中,Call stack是指存放某个程序的正在运行的函数的信息的 ...

  6. 【BZOJ2749】【HAOI2012】外星人[欧拉函数]

    外星人 Time Limit: 3 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Input   Output 输出te ...

  7. string字符串js操作

    String 对象方法 方法 描述 anchor() 创建 HTML 锚. big() 用大号字体显示字符串. blink() 显示闪动字符串. bold() 使用粗体显示字符串. charAt() ...

  8. HDU 1162 Eddy's picture (最小生成树 prim)

    题目链接 Problem Description Eddy begins to like painting pictures recently ,he is sure of himself to be ...

  9. hud 2554 N对数的排列问题 (规律)

    题目链接 Problem Description 有N对双胞胎,他们的年龄分别是1,2,3,--,N岁,他们手拉手排成一队到野外去玩,要经过一根独木桥,为了安全起见,要求年龄大的和年龄小的排在一起,好 ...

  10. 利用QT、QWebview、ffmpeg实现的屏幕录制方案

    .katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...