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).求出最后还能看见多少张海报. 分析 ...
随机推荐
- 自己查与写的批量比较bash
前言:互测的时候一个一个输入感觉太麻烦,于是尝试写自己的对拍,又想到os刚学了bash命令行处理,于是想把两者结合一下减轻自己的工作量 分两步: 将所有人的工程导出成jar文件 放到linux下用ba ...
- 【猫狗数据集】谷歌colab之使用pytorch读取自己数据集(猫狗数据集)
之前在:https://www.cnblogs.com/xiximayou/p/12398285.html创建好了数据集,将它上传到谷歌colab 在colab上的目录如下: 在utils中的rdat ...
- 026.掌握Service-外部访问
一 集群外部访问 由于Pod和Service都是Kubernetes集群范围内的虚拟概念,所以集群外的客户端默认情况,无法通过Pod的IP地址或者Service的虚拟IP地址:虚拟端口号进行访问.通常 ...
- 用libvlc 抓取解码后的帧数据
vlc是一套优秀的开源媒体库,其特点是提供了完整的流媒体框架, 用它可以非常方便的实现抓取解码帧的功能. 与此功能有关的关键API为 libvlc_video_set_callbacks /*设置回调 ...
- javaee作业
一.单选题(共5题,50.0分) 1 在SqlSession对象的openSession()方法中,不能作为参数executorType的可选值 的是( ). A. ExecutorTyp ...
- vux中表单验证,在提交时自动聚焦到未验证通过的那栏;及循环表单的验证
首先vux中的表单验证在点击触发,失焦时才显示错误信息,如果不管它,它就没反应,这显然是不合理的:解决办法就是:在提交时做验证,不通过的话就使用.focus()及.blur()方法给它聚焦,失焦. i ...
- iframe 框架父页面刷新子页面
1.父页面添加: <script> function testBtn(){ var reshSrc = document.getElementById('myFrame').src; ...
- 记一次nor flash固件烧录速度优化
背景 某个方案使用的是spinor作为存储介质,每次烧录新固件都耗时数分钟,为了提高效率,需要对其进行优化. 分析流程 基本流程 当前烧录流程,有一个可选步骤,全盘擦除,这个步骤耗时达数分钟.不过这是 ...
- webService-axis开发jar包
开发最小jar包集: activation-1.1.jar axiom-api-1.2.13.jar axiom-dom-1.2.13.jar axiom-impl-1.2.13.jar axis2- ...
- 阿里淘宝的S1级别bug,到底是谁的锅?
3月25日,阿里的淘宝APP在IOS系统上出现BUG: 在打开淘宝APP以后,用户就会收到系统弹窗通知:“您使用的程序是测试/内测版本,将于当地时间2020-03-28到期,到期后将无法使用,请尽快下 ...