H - 覆盖的面积(线段树-线段扫描 + 离散化(板题))
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.
注意:本题的输入数据较多,推荐使用scanf读入数据.
Output
对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.
知识点简介
- 线段树扫描可以横向扫描水平的边,也可纵向扫描竖直的边。
- 这里我们们只讨论 扫描水平边的情况,我们先引入两个概念:
上行边、下行边
,上行边就是某个矩形上边的那条边,下行边就是某个举行下边的那条边。 - 线段树扫描线段树的本质:就是将所有的给的矩形,把它们的水平线一一的抽出来,按水平线的高度从下到上一一添加边,在添加水平边的时候,我们要不断维护扫描线在x轴的有效长度,我们每添加一条下行边,那么这个我们就要在这个扫描线所在x轴上的区间的所有的sum[ ](
这里的的sum其实是区间和
)加1(如下图扫描线1:在(10,20)的区间的值都加1 ->sum[10,20]),如过每增加一条上行边(如:扫描线3),那么所有的相应的区间sum都减1,而这样通过这样不断的添加扫描线通过sum[1]来获取总的当前总的扫描线在x轴上的有效扫描面积,而两条扫描之间的高度差是很容易得到的,这样通 两条扫描的高度差 x 扫描线在x轴上的有效距离 就可求出两条扫描线之间的有效距离了。通过不断的添加扫描线并重复上述过程就可得到总的有效面积了 - 线段树的有一个千万要注意的地方: 线段树的叶节点存储的是什么?? 如果不考虑离散化的话:叶节点存储的是 矩形的端点横坐标(排序后的),这样通过两个叶节点相减(横坐标相减),我们就能得到一个长度区间,而这个区间有可能是扫描线在x轴有效长度的一个组成部分,这样同多个相应的叶节点横坐标相减就能够得到 总的扫描的线的有效长度
- 还有一个要注意的地方:就是当我代入右边界r到 Update() 函数的时候我们带的是 r - 1, 而在Push_up()函数中用到的又是
(r - 1) + 1
,这个地方希望我们都仔细想想。。理解理解
dalao线段树讲解传送门
接下来继续贴几张别人的图来帮助自己理解:
代码一(扫描线)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define rtr rt << 1 | 1
#define rtl rt << 1
const int maxn = 10005;
struct Line
{
double l, r, h;
int d;
bool operator < (const Line & a) const
{
return h < a.h;
}
} line[maxn];
int mrk[maxn << 2];
double sum1[maxn << 2];
double sum2[maxn << 2];
double X[maxn];
void init()
{
memset(mrk, 0, sizeof(mrk));
memset(sum1,0, sizeof(sum1));
memset(sum2,0, sizeof(sum2));
}
void Push_up(int rt, int l, int r)
{
if(mrk[rt]) sum1[rt] = X[r + 1] - X[l];
else if(l == r) sum1[rt] = 0;
else sum1[rt] = sum1[rtl] + sum1[rtr];
if(mrk[rt] >= 2) sum2[rt] = X[r + 1] - X[l];
else if(l == r) sum2[rt] = 0;
else if(mrk[rt] == 1) sum2[rt] = sum1[rtl] + sum1[rtr];
else sum2[rt] = sum2[rtl] + sum2[rtr];
}
void Update(int rt, int l, int r, int s, int e, int d)
{
if(s <= l && r <= e)
{
mrk[rt] += d;
Push_up(rt ,l ,r);
return;
}
int m = l + r >> 1;
if(s <= m) Update(rtl, l, m, s, e, d);
if(e > m) Update(rtr, m+1, r, s, e, d);
Push_up(rt, l, r);
}
int Sch(double val, double X[], int len)
{
int l = 0, r = len - 1;
while(l <= r)
{
int m = (l + r) >> 1;
if(X[m] == val) return m;
if(X[m] > val) r = m - 1;
else l = m + 1;
}
return -1;
}
int main()
{
//freopen("A.txt","r",stdin);
int t;
scanf("%d", &t);
while(t --)
{
init();
int n;
scanf("%d", &n);
double x1, y1, x2, y2;
int a = 0;
for(int i = 1; i <= n; i ++)
{
scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
X[a] = x1;
line[a ++] = (Line){ x1, x2, y1, 1};
X[a] = x2;
line[a ++] = (Line){ x1, x2, y2, -1};
}
//排序
sort(X, X + a);
sort(line, line + a);
//离散化
int b = 1;
for(int i = 1; i < a; i ++)
if(X[i] != X[i - 1])
X[b ++] = X[i];
//遍历讨论
double ans = 0;
for(int i = 0; i < a - 1; i ++)
{
int s = Sch(line[i].l, X, b);
int e = Sch(line[i].r, X, b) - 1;
Update(1, 0, b-1, s, e, line[i].d);
ans += sum2[1] * (line[i+1].h - line[i].h);
}
printf("%.2lf\n", ans);
}
return 0;
}
代码二(暴力递归下方标记)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define db double
#define rtl rt << 1
#define rtr rt << 1 | 1
const int mxn = 10005;
struct Line
{
db l, r, h;
int d;
bool operator < (const Line &a) const
{
return h < a.h;
}
} line[mxn];
struct Node
{
int cnt, lazy;
db sum;
} tre[mxn << 2];
db X[mxn];
void Push_up(int rt, int l, int r)
{
if(tre[rt].cnt >= 2) tre[rt].sum = X[r + 1] - X[l];
else if(l == r) tre[rt].sum = 0;
else tre[rt].sum = tre[rtl].sum + tre[rtr].sum;
}
void Push_down(int rt, int l, int r)
{
if(! tre[rt].lazy) return;
tre[rtl].cnt += tre[rt].lazy;
tre[rtr].cnt += tre[rt].lazy;
tre[rtl].lazy += tre[rt].lazy;
tre[rtr].lazy += tre[rt].lazy;
tre[rt].lazy = 0;
}
void Build(int rt, int l, int r)
{
tre[rt].cnt = tre[rt].lazy = tre[rt].sum = 0;
if(l == r) return;
int m = (l + r) >> 1;
Build(rtl, l, m);
Build(rtr, m+1, r);
}
void Update(int rt, int l, int r, int s, int e, int d)
{
if(s <= l && r <= e)
{
tre[rt].cnt += d;
tre[rt].lazy += d; //这个区间更新了,打一个标记
Push_up(rt, l , r); //更行这个区间是因为,父区间可能用得到
return;
}
Push_down(rt, l, r);
int m = (l + r) >> 1;
if(s <= m) Update(rtl, l, m, s, e, d);
if(e > m) Update(rtr, m+1, r, s, e, d);
Push_up(rt, l, r);
}
void Query(int rt, int l, int r, int s, int e)
{
if(l == r)
{
Push_up(rt, l, r);
return;
}
Push_down(rt, l, r); //查询的时候将所有经过的层下放
int m = (l + r) >> 1;
if(s <= m)
Query(rtl, l, m, s, e);
if(e > m)
Query(rtr, m+1, r, s, e);
Push_up(rt, l, r);
}
int Sch(db val, db X[], int len)
{
int l = 0, r = len - 1;
while(l <= r)
{
int m = (l + r) >> 1;
if(X[m] == val) return m;
if(X[m] > val) r = m - 1;
else l = m + 1;
}
return -1;
}
int main()
{
//freopen("A.txt","r",stdin);
int t;
scanf("%d", &t);
while(t --)
{
int n;
scanf("%d", &n);
db x1, y1, x2, y2;
int a = 0;
for(int i = 1; i <= n; i ++)
{
scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
X[a] = x1;
line[a ++] = (Line){ x1, x2, y1, 1 };
X[a] = x2;
line[a ++] = (Line){ x1, x2, y2, -1 };
}
sort(X, X + a);
sort(line, line + a);
int b = 1;
for(int i = 1; i < a ; i ++)
if(X[i] != X[i - 1])
X[b ++] = X[i];
Build(1, 0, b - 1);
db ans = 0;
for(int i = 0; i < a - 1; i ++)
{
int s = Sch(line[i].l, X, b);
int e = Sch(line[i].r, X, b) - 1;
Update(1, 0, b-1, s, e, line[i].d);
Query(1, 0, b-1, s, e);
ans += tre[1].sum * (line[i + 1].h - line[i].h);
}
printf("%.2lf\n", ans);
}
return 0;
}
H - 覆盖的面积(线段树-线段扫描 + 离散化(板题))的更多相关文章
- POJ-2528 Mayor's posters (线段树区间更新+离散化)
题目分析:线段树区间更新+离散化 代码如下: # include<iostream> # include<cstdio> # include<queue> # in ...
- 权值线段树&线段树合并
权值线段树 所谓权值线段树,就是一种维护值而非下标的线段树,我个人倾向于称呼它为值域线段树. 举个栗子:对于一个给定的数组,普通线段树可以维护某个子数组中数的和,而权值线段树可以维护某个区间内数组元素 ...
- 覆盖的面积(HDU 1255 线段树)
覆盖的面积 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Problem D ...
- 【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)
3589: 动态树 Time Limit: 30 Sec Memory Limit: 1024 MBSubmit: 405 Solved: 137[Submit][Status][Discuss] ...
- 【LeetCode】线段树 segment-tree(共9题)+ 树状数组 binary-indexed-tree(共5题)
第一部分---线段树:https://leetcode.com/tag/segment-tree/ [218]The Skyline Problem [307]Range Sum Query - Mu ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
- POJ2528:Mayor's posters(线段树区间更新+离散化)
Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral electio ...
- hihoCoder #1079 : 离散化 (线段树,数据离散化)
题意:有一块宣传栏,高一定,给出长度,再给出多张海报的张贴位置,问还能见到几张海报(哪怕有一点被看到)?假设海报的高于宣传栏同高. 思路:问题转成“给出x轴上长为L的一条线段,再用n条线段进行覆盖上去 ...
- POJ 2528 Mayor’s posters (线段树段替换 && 离散化)
题意 : 在墙上贴海报, n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000).求出最后还能看见多少张海报. 分析 ...
随机推荐
- 讨论一下.NET里,对cookie身份验证的超时的处理
引言 在.NET里提供了FormsAuthentication类用来对用户身份进行验证和授权.不过,对于cookie的超时处理,一直是一个头疼的问题.这里介绍一下微软对.NET 身份验证超时的处理机制 ...
- Win10系统下安装tensorflow(cpu)+keras+jupyter notebook运行环境
记录,自用 1.安装Anaconda(这里安装的是python3.6版本) 2.创建tensorflow的conda环境 conda create -n tensorflow python=3.6 3 ...
- 分布式图数据库 Nebula Graph 的 Index 实践
导读 索引是数据库系统中不可或缺的一个功能,数据库索引好比是书的目录,能加快数据库的查询速度,其实质是数据库管理系统中一个排序的数据结构.不同的数据库系统有不同的排序结构,目前常见的索引实现类型如 B ...
- 蓝牙技术 A2DP AVRCP BlueZ
BlueZ 做为 linux 标准的协议栈,提供非常多的 profile ,各种的支持,ble , 蓝牙网络,文件传输,a2dp 音频传输. A2DP——Advanced Audio Distribu ...
- 编译 ijg JPEG V8 库 GIF 库
libjpeg-turbo-1.2.1太老了,不支持,从内存解压,这里编译支持 jpeg_mem_src 的 JPEG V9 wget http://www.ijg.org/files/jpegsrc ...
- Java集合01——List 的几个实现类,了解一下?
从本文起,我们将开始分享 Java 集合方面的知识,关注公众号「Java面典」了解更多 Java 知识点. List 是继承于 Collection 的接口,其实现类有 ArrayList,Linke ...
- Simulink仿真入门到精通(十三) Simulink创建自定义库
当用户自定义了一系列模块之后,可以自定义模块库将同类自定义模块显示到Simulink Browser中,作为库模块方便地拖曳到新建模型中. 建立这样的自定义库需要3个条件: 建立library的mdl ...
- Simulink仿真入门到精通(十二) Publish发布M文件
12.1 M文件的注释 使用%进行注释. 连续多行注释Ctrl+R,取消注释Ctrl+T. 12.2 Cell模式 在MATLAB脚本文件中使用连续两个注释符,开启一个新的Cell块,%%后空一格追加 ...
- 一键配置openstack-cata版的在线yum源
下面脚本可以直接复制来配置openstack-ocata版的yum源: echo "nameserver 8.8.8.8 nameserver 119.29.29.29 nameserver ...
- vue项目中使用Lodop实现批量打印html页面和pdf文件
1.Lodop是什么? Lodop(标音:劳道谱,俗称:露肚皮)是专业WEB控件,用它既可裁剪输出页面内容,又可用程序代码直接实现复杂打印.控件功能强大,却简单易用,所有调用如同JavaScript扩 ...