HDU1542-Atlantis【离散化&线段树&扫描线】个人认为很全面的详解
刚上大一的时候见过这种题,感觉好牛逼哇,这都能算
如今已经不打了,不过适当写写题保持思维活跃度还是不错的,又碰到这种题了,想把它弄出来
说实话,智商不够,看了很多解析,花了4、5个小时才弄明白
网上好多都是直说一半,弄得我很难受,需要查看很多题解不断对比才清楚
首先线段树这玩意,不光是线段树吧,只要牵扯到递归都很抽象,要想好久
如果中途有哪些不懂,继续看,代码我尽量做到每一行都有注释
1.离散化
先说离散化,这里面牵扯到小数,而线段树是维护一个整数区间,这是我们首先遇到的问题

比如这种情况,第二个矩形刚好多了0.5怎么办哦
这时候就要用离散化来处理了,我个人感觉离散化说白了就是映射,先把一些难算的数值映射到一些简单的数值上算完了再换回去
比如解物理题的时候先把一些不能拆开但很复杂的表达式用符号代替,算完了再换回去
那么两个矩形四条竖边,对应的x分别是10、15、20、25.5,那么久把这个不同的x值,存到数组里dif_x[4] = {10,15,20,25.5}
用的时候,比如你要用到15~25.5这条边的时候就用dif_x[3] - dif_x[1]得到10.5,就ok了
2.扫描线
你就想象有一根水平的线从下往上走,碰到边就更新,这个有前人说的很详细,我就直接拿来用了
http://www.cnblogs.com/Konjakmoyu/p/6050343.html
这个讲的很好,我一开始理解扫描线就是看的这个
3.线段树
首先你要有线段树的基本知识嘛,这个就不说了
说一下里面值的定义
struct node2
{
int l,r,cnt;
double len;
}tree[];
这个也是这道题里面最难理解的一部分
线段树到底维护的什么,或者说存放的什么?
首先,根据扫描线的思路,你要存放的是当前扫描线的长度,这样才能乘高度差

那么你每次要更新的就是蓝线的长度
那么蓝线的长度是不是可以由 dif_x[r] - dif_x[l]得到
比如我们第一次更新的这条蓝线是10~20,那么是不是可以由dif_x[] 数组的下标0~2来表示
第二次更新的蓝线是10~25.5,是不是可以由0~3来表示
最后一次由1~3表示
那我们就知道了,线段树里的l,r其实存的是该结点所能涉及的dif_x[]数组的下标

结点1的l,r分别是0,3那么就是10——25.5这条线段中,被覆盖的长度
但是这里就有一个问题了,0——1的长度可以理解,但是0是一个点,它的长度就是0啊
所以我么规定,每一个坐标表示从它到它+1的点的长度
比如tree[4]表示,0~1的长度是5
tree[6]表示,2~3的长度是5.5
cnt表示该结点是否被覆盖,参与这次面积的计算
double len 当然表示的就是该线段的长度了
补充
还需要一个比较重要的结构体
struct node
{
double x1,x2,y;
int flag;
void init(double l,double r,double h,int key) {
x1 = l; x2 = r; y = h; flag = key;
}
}line[];
这个结构体代表每一根横线

比如灰线代表line[0],红线line[1],粉线line[2]以此类推
x1,x2就是这根线的左右端点坐标,y是这根线的y轴坐标,flag表示这根线是矩形的下面的线还是上面的线
这个很重要,如果是下面的线,在更新tree[]的时候,由于你是从下往上扫,所以要+1
如果是上面的线,在更新线段树tree[]的时候,由于你是从下往上扫,所以要-1,表示你已经扫完了某个矩形,你要将矩形的上边减掉
要讲的就这么多了吧
看代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; double dif_x[];//记录不同的x坐标 struct node
{
double x1,x2,y;
int flag;//各个参数上面说过
void init(double l,double r,double h,int key) {
x1 = l; x2 = r; y = h; flag = key;
}
}line[]; bool cmp(node a, node b)
{
return a.y < b.y;
} struct node2
{
int l,r,cnt;//各个参数上面说过
double len;
}tree[]; void build_tree(int id,int l,int r)//最基本的线段树建树不多说
{
tree[id].l = l;
tree[id].r = r;
tree[id].cnt = ;
tree[id].len = ;
if(l==r){
return;
}
int mid = (r + l)>>;
build_tree(id<<,l,mid);
build_tree((id<<)+,mid+,r);
} void getlen(int id)
{
if(tree[id].cnt >= ) { //如果该段被覆盖那么就直接由dif_x数组获得长度
tree[id].len = dif_x[tree[id].r+] - dif_x[tree[id].l];//看了注释①以后,应该不难理解了
}
else {
tree[id].len = tree[id<<].len + tree[(id<<)|].len;//如果没有被覆盖,那么应该是由左右孩子的和
}
} void update(int id,int l,int r,int v)
{
if(tree[id].l==l && tree[id].r==r) {//目标区间
tree[id].cnt += v;//标记是否覆盖
getlen(id);//算一下长度
return;
}
int mid = (tree[id].l+tree[id].r)>>;
if(r <= mid){
update(id<<,l,r,v);//更新左子树
}
else if(l > mid) {
update((id<<)+,l,r,v);//更新右子树
}
else {
update(id<<,l,mid,v);
update((id<<)+,mid+,r,v);//更新左右子树
}
getlen(id);//push_up一下
} int mySearch(double p, int l, int r)//二分找p在dif_x数组中的下标
{
while(l <= r)
{
int mid = (l + r)>>;
if(dif_x[mid] == p){
return mid;//返回这个下标
}
if(dif_x[mid] < p) {
l = mid + ;
}
else {
r = mid - ;
}
}
} int main()
{
int n,noc = ;//number_of_case
while(~scanf("%d",&n))//输入
{
if (n == ) break;
noc ++;
double x1,y1,x2,y2;//矩形的位置参数
int line_num = ;//一共有多少条横线
for(int i=;i<n;i++)//输入
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line[line_num].init(x1,x2,y1,);//下面的线,flag = 1
dif_x[line_num++] = x1;//数据中一共有多少个不同的横坐标
line[line_num].init(x1,x2,y2,-);//上面的线,flag = -1
dif_x[line_num++] = x2;
}
sort(line, line+line_num, cmp);//对横线由低到高进行排序,因为你是从下往上扫描的
sort(dif_x, dif_x+line_num);//对dif_x去重,要先排个序,这样更方便
int dif_x_num = unique(dif_x, dif_x+line_num) - dif_x;//dif_x_num表示去重后不同的x坐标的数量
build_tree(,,dif_x_num-);//建立线段树
double ans = ;//最终的答案
for(int i=;i<line_num-;i++)//开始扫描
{
int line_l = mySearch(line[i].x1,,dif_x_num-);//第i根线的左端点对应在dif_x的下标
int line_r = mySearch(line[i].x2,,dif_x_num-)-;//右边要减一,看注释①
update(,line_l,line_r,line[i].flag);//更新线段树
ans += tree[].len * (line[i+].y - line[i].y);//求面积,tree[1]就是总长度嘛
}
printf("Test case #%d\n",noc);
printf("Total explored area: %.2lf\n\n",ans);
}
}
注释① 这里我看了n多题解,没一个说为什么-1,管这个我就懵逼了70%的时间,希望看了这个会节约你很多时间
其实我前面已经说了,每个点代表从它到它+1的点的长度,那么你要求0~3的长度,其实要求的是0~2的长度 好辛苦,终于写完了,希望对你有帮助!
HDU1542-Atlantis【离散化&线段树&扫描线】个人认为很全面的详解的更多相关文章
- hdu1542 Atlantis (线段树+扫描线+离散化)
Atlantis Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total S ...
- hdu-1542 Atlantis(离散化+线段树+扫描线算法)
题目链接: Atlantis Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- Atlantis poj1151 线段树扫描线
Atlantis poj1151 线段树扫描线 题意 题目给了n个矩形,每个矩形给了左下角和右上角的坐标,矩形可能会重叠,求的是矩形最后的面积. 题解思路 这个是我线段树扫描线的第一题,听了学长的讲解 ...
- HDU 4419 Colourful Rectangle --离散化+线段树扫描线
题意: 有三种颜色的矩形n个,不同颜色的矩形重叠会生成不同的颜色,总共有R,G,B,RG,RB,GB,RGB 7种颜色,问7种颜色每种颜色的面积. 解法: 很容易想到线段树扫描线求矩形面积并,但是如何 ...
- HDU 1542 Atlantis(线段树扫描线+离散化求面积的并)
Atlantis Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total S ...
- poj1151 Atlantis (线段树+扫描线+离散化)
有点难,扫描线易懂,离散化然后线段树处理有点不太好理解. 因为这里是一个区间,所有在线段树中更新时,必须是一个长度大于1的区间才是有效的,比如[l,l]这是一根线段,而不是区间了. AC代码 #inc ...
- [POJ1151][HDU1542]Atlantis(线段树,扫描线)
英文题面,我就只放个传送门了. Solution 题意是算矩形面积并,这是扫描线算法能解决的经典问题. 算法的大致思想是,把每一个矩形拆成上边和下边(以下称作扫描线),每条扫描线有四个参数l,r,h ...
- HDU 1542 矩形面积并【离散化+线段树+扫描线】
<题目链接> 题目大意: 给你n个矩形,求出它们面积的并. 解题分析: 此题主要用到了扫描线的思想,现将各个矩形的横坐标离散化,然后用它们离散化后的横坐标(相当于将矩形的每条竖线投影在x轴 ...
- P - Atlantis (线段树+扫描线)
There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Som ...
随机推荐
- Prometheus & SoundCloud
Prometheus 系统监控方案 一 - Vovolie - 博客园https://www.cnblogs.com/vovlie/p/Prometheus_CONCEPTS.html Prometh ...
- 企业级分布式应用服务EDAS _Dubbo商业版_微服务PaaS平台 【EDAS Serverless 运维 创业】
企业级分布式应用服务EDAS _Dubbo商业版_微服务PaaS平台_分布式框架 - 阿里云https://www.aliyun.com/product/edas?source_type=yqzb_e ...
- 阿里云CodePipeline vs Jenkins
产品概述_产品简介_CodePipeline-阿里云 https://help.aliyun.com/document_detail/56512.html CodePipeline管理控制台https ...
- 【Python3练习题 017】 两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比。请编程序找出三队赛手的名单。
import itertools for i in itertools.permutations('xyz'): if i[0] != 'x' and i[2] != 'x' and i[ ...
- C#的类型推断发展史
前言:随着C#的版本升级,C#编译器的类型推断功能也在不断的升级以适应语言进化过程中的变化,并为这个过程做了相应的优化. 隐式类型的数组 在C#1和C#2中,作为变量声明和初始化的一部分,初始化数组的 ...
- Java Integer 与 int 深刻理解
今天在做Object 自动转为Integer 类型之后的判断,遇到一个不理解的点,当数值超过127之后,两个数值相同的Object 对象用 == 判断的结果是false. Object a = 128 ...
- K8S集群 NOT READY的解决办法 1.13 错误信息:cni config uninitialized
今天给同事 一个k8s 集群 出现not ready了 花了 40min 才搞定 这里记录一下 避免下载 再遇到了 不清楚. 错误现象:untime network not ready: Networ ...
- zepto的extend
类型判断 var class2type = {},toString = class2type.toString,$={}; //判断类型 function type(obj) { return obj ...
- Netty派生缓冲区
参考https://blog.csdn.net/wangjinnan16/article/details/77972113 派生缓冲区 派生缓冲区,也就是创建一个已经存在的缓冲区的视图,可以调用dup ...
- yum仓库搭建
1. 创建yum仓库目录 mkdir -p /application/yum/centos6.6/x86_64/ cd /application/yum/centos6.6/x86_64/ rz # ...