hdu 1542 线段树+扫描线 学习
学习扫描线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 线段树+扫描线 学习的更多相关文章
- HDU 1542 线段树+扫描线+离散化
Atlantis Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Su ...
- Atlantis HDU - 1542 线段树+扫描线 求交叉图形面积
//永远只考虑根节点的信息,说明在query时不会调用pushdown //所有操作均是成对出现,且先加后减 // #include <cstdio> #include <cstri ...
- hdu 4052 线段树扫描线、奇特处理
Adding New Machine Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe ...
- hdu 1542 线段树扫描(面积)
Atlantis Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Su ...
- hdu 1828 线段树扫描线(周长)
Picture Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Sub ...
- hdu 5091(线段树+扫描线)
上海邀请赛的一道题目,看比赛时很多队伍水过去了,当时还想了好久却没有发现这题有什么水题的性质,原来是道成题. 最近学习了下线段树扫描线才发现确实是挺水的一道题. hdu5091 #include &l ...
- HDU 5107 线段树扫描线
给出N个点(x,y).每一个点有一个高度h 给出M次询问.问在(x,y)范围内第k小的高度是多少,没有输出-1 (k<=10) 线段树扫描线 首先离散化Y坐标,以Y坐标建立线段树 对全部的点和询 ...
- hdu 1542 线段树之扫描线之面积并
点击打开链接 题意:给你n个矩形,求它们的面积,反复的不反复计算 思路:用线段树的扫描线完毕.将X坐标离散化后,从下到上扫描矩形,进行各种处理,看代码凝视把 #include <stdio.h& ...
- hdu 1255(线段树 扫描线) 覆盖的面积
http://acm.hdu.edu.cn/showproblem.php?pid=1255 典型线段树辅助扫描线,顾名思义扫描线就是相当于yy出一条直线从左到右(也可以从上到下)扫描过去,此时先将所 ...
随机推荐
- Nginx核心配置文件常用参数详解
Nginx核心配置文件常用参数详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 关于Nginx权威文档的话童鞋们可以参考Nginx官方文档介绍:http://nginx.org/ ...
- Object.keys()返回对象的属性
<script> // 传入对象,返回属性名 let obj = { 'a': '123', 'b': '456' } console.log(Object.keys(obj)) //[& ...
- JS 求一组数中所有数的和以及平均值
var arr = [10,20,30]; //计数器思想 var sum = 0; for(var i=0;i<arr.length;i++){ sum += arr[i]; } consol ...
- JVM调优命令-jstack
jstack jstack用于生成java虚拟机当前时刻的线程快照.线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁 ...
- TCP粘包,拆包及解决方法
在进行Java NIO学习时,发现,如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况,这就是TCP协议中经常会遇到的粘包以及拆包的问题.我们都知道TCP属于传输 ...
- GYM 101173 K.Key Knocking(构造)
原题链接 参考自 问题描述:一个长度为3*n的01串,每次可以翻转连续的两个字符,要求至多翻转n次使得这个3*n的串至少有2*n个连续的段且相邻两端不一样(就是连续的0算一段,然后连续的1,…) 解法 ...
- 将文本(lrc,txt)文件转换成UTF-8格式
UTF-8是UNICODE的一种变长字符编码又称万国码,由Ken Thompson于1992年创建.现在已经标准化为RFC 3629.UTF-8用1到6个字节编码UNICODE字符.用在网页上可以同一 ...
- 第16月第6天 vs2005 lseek directdraw
1. //_lseek(file_handle, -(int)pbitmap->bitmapinfoheader.biSizeImage, SEEK_END); SetFilePointer(( ...
- 在虚拟机安装windows xp时所需要的序列号
最新的windows xp sp3序列号 xp序列号 最新的windows xp sp3序列号(绝对可通过正版验证) MRX3F-47B9T-2487J-KWKMF-RPWBY(工行版) 可用(强推 ...
- 深入解析Java AtomicInteger 原子类型
深入解析Java AtomicInteger原子类型 在进行并发编程的时候我们需要确保程序在被多个线程并发访问时可以得到正确的结果,也就是实现线程安全.线程安全的定义如下: 当多个线程访问某个类时,不 ...