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).求出最后还能看见多少张海报. 分析 ...
随机推荐
- 关于使用Binlog和canal来对MySQL的数据写入进行监控
先说下Binlog和canal是什么吧. 1.Binlog是mysql数据库的操作日志,当有发生增删改查操作时,就会在data目录下生成一个log文件,形如mysql-bin.000001,mysql ...
- Redis系列四 - 分布式锁的实现方式
前言 分布式锁一般有3中实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 以下将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁的可 ...
- java中的对象 方法 引用 等一些抽象的概念是什么意思呢?
2020-03-14 最近这一段时间有点忙,好久都没有更新博客了,之后我会一直坚持下去的,和大家一同进步的. 这段时间一直在学java,相信刚开始学习java的小白,刚开始接触那么些抽象的概念一定和我 ...
- 代号为 Kyria 的 Manjaro Linux 19.0 系统正式发布
Xfce版本仍然是主打,此版本Xfce更新到4.14,并且主要致力于在桌面和窗口管理器上完善用户体验. KDE版本提供了功能强大.成熟且丰富的Plasma 5.17桌面环境,此版本进行了完全重新设计. ...
- 【5min+】 一个令牌走天下!.Net Core中的ChangeToken
系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...
- C++ 【静态成员】static修饰的成员
首先,我们先通过字面意思来理解... 成员:成员变量.成员函数. static 修饰成员变量,还有修饰成员函数. static 声明为静态的,称为静态成员.不管这个类创建了多少个对象,静态成员只有 ...
- 【Weiss】【第03章】练习3.20:中缀表达式转后缀表达式
[练习3.20] a.编写一个程序将中缀表达式转换为后缀表达式,该中缀表达式含括号及四则运算. b.把幂操作符添加到你的指令系统中去. c.编写一个程序将后缀表达式转化为中缀表达式. Answer: ...
- JavaScript(7)--- 继承
JavaScript(7)--- 继承 概念 首先继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承,继承也是为了数据共享. 之间有讲过j ...
- VUE npm run dev 启动时,报了一大堆错误 Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 7.x
npm run dev 启动时,报了一大堆错误 Module build failed: Error: Missing binding E:\2017VocaSchool\vocationWeb\no ...
- Model、Form、ModelForm的比较
Model.Form.ModelForm 本节内容: 1:Model 2:Form 3:Model Form 1 2 3 http://www.cnblogs.com/wupeiqi/articles ...