hdu1828 Picture(线段树+扫描线+矩形周长)
看这篇博客前可以看一下扫描线求面积:线段树扫描线(一、Atlantis HDU - 1542(覆盖面积) 二、覆盖的面积 HDU - 1255(重叠两次的面积))
解法一·:两次扫描线
如图我们可以先用扫描线找出来横线的周长和,再用扫描线找纵线周长和
这里以横线来举例:
横线的长度 = 【现在这次总区间被覆盖的程度和上一次总区间被覆盖的长度之差的绝对值】
下面有一点要注意的(参考链接:https://www.cnblogs.com/violet-acmer/p/11461660.html)
需要注意的是,在求解矩形面积并的时候,排序策略是按照 h 从小到大排序,并不需要考虑 h 相同的情况;
但是求矩形周长并的时候就不行了,必须得考虑 h 相同的情况下的排序策略;
以下例求解横线并的长度为例;
n=2
(2,1),(4,3)
(3,3),(4,5)
假设与处理矩形面积并的排序策略相同,只考虑按 h 升序排列,那么排列后的数据为:
e1:{l=2 , r=4 , h=1 , f=1}e1:{l=2 , r=4 , h=1 , f=1}
e2:{l=2 , r=4 , h=3 , f=−1}e2:{l=2 , r=4 , h=3 , f=−1}
e3:{l=3 , r=4 , h=3 , f=1}e3:{l=3 , r=4 , h=3 , f=1}
e1:{l=3 , r=4 , h=5 , f=−1}e1:{l=3 , r=4 , h=5 , f=−1}
让我们手动模拟一下这个求解过程;
定义 ans 表示答案,len 表示当前更新后的横线并的总长度,pre 表示上一次更新后的 len 值;
初始化 ans=0,len=0,pre=0;
首先处理 e1e1,update() 后,len = 2;
更新 ans += |len-pre| = 2 , pre=len=2;
处理 e2e2,update() 后,len = 0;
此时,更新 ans += |len-pre| = 2+2 = 4 , pre=len=0,出现问题了是吧,因为来到此处的时候,ans=3才对;
继续向上更新, e3e3,update() 后,len=1;
更新 ans += |len-pre| = 4+1 = 5 , pre=len=1;
e4e4,update() 后,len=0;
更新 ans += |len-pre| = 5+1 = 6;
但是正确答案应该是 ans = 4;
多的 2 就是因为排序策略不当而产生的;
所以,当 h 相同的时候,应该先处理 f = 1 的边,即如果可以先加入边,那就优先执行加边操作;
第二种解法:参考:https://blog.csdn.net/qq3434569/article/details/78220821
看出什么了吗?这张图在上面那张图的基础上多了几条竖线。
我的意思是说,我们可以只做一次自下而上的扫描就把横线竖线都算出来!
竖线的算法和上面说的方法一样:【现在这次总区间被覆盖的程度和上一次总区间被覆盖的长度之差的绝对值】
竖线要怎么计算?
首先我们现在改一下线段树保存的属性,我们用如下信息记录线段树的节点:
1. l , r : 该节点代表的线段的左右端点坐标
2.len : 这个区间被覆盖的长度(即计算时的有效长度)
3.s : 表示这个区间被覆盖了几次
4. lc , rc : 标记这个节点的左右两个端点是否被覆盖(0表示没有,1表示有)
5.num :这个区间有多少条线段(这个区间被多少条线段覆盖)
这里的num涉及到竖线的计算,故解释一下,举几个例子:
若区间[0,10]被[1,2][4,5]覆盖,则num = 2
若区间[0,10]被[1,3][4,5]覆盖,则num = 1(两区间刚好连在一起)
若区间[0,10]被[1,5][2,6]覆盖,则num = 1(两区间连起来还是一段)
然后就可以计算竖线了:
竖线的长度 = 【下一条即将被扫到的横线的高度 - 现在扫到的横线的高度】*2*num
乘2是因为每条线段有两个端点;
看上图中棕色线段的竖线有4条,因为棕色的横线由2条线段组成
白色线段的竖线只有2条,因为白色的横线由1条线段组成(虽然这1条线段是由许多线段组合而成,但它依旧只算1条线段)
这样,依旧只扫一次就可以算出周长。
本题用第二种方法写的代码:
1 #include <cstdio>
2 #include <cstring>
3 #include <cctype>
4 #include <algorithm>
5 using namespace std;
6 #define lson l , m , rt << 1
7 #define rson m + 1 , r , rt << 1 | 1
8 const int maxn = 22222;
9 struct Seg
10 {
11 int l, r, h, s;
12 Seg() {}
13 Seg(int a, int b, int c, int d) :l(a), r(b), h(c), s(d) {}
14 bool operator < (const Seg &cmp) const
15 {
16 return h < cmp.h;
17 }
18 } ss[maxn];
19 bool lbd[maxn << 2], rbd[maxn << 2];//标记这个节点的左右两个端点是否被覆盖(0表示没有,1表示有)
20 int numseg[maxn << 2];//这个区间有多少条线段(这个区间被多少条线段覆盖)
21 int cnt[maxn << 2];//表示这个区间被重复覆盖了几次
22 int len[maxn << 2];//这个区间被覆盖的长度
23 void PushUP(int rt, int l, int r)
24 {
25 if (cnt[rt]) //整个区间被覆盖
26 {
27 lbd[rt] = rbd[rt] = 1;
28 len[rt] = r - l + 1;
29 numseg[rt] = 2;//每条线段有两个端点
30 }
31 else if (l == r) //这是一个点而不是一条线段
32 {
33 len[rt] = numseg[rt] = lbd[rt] = rbd[rt] = 0;
34 }
35 else //是一条没有整个区间被覆盖的线段,合并左右子的信息
36 {
37 lbd[rt] = lbd[rt << 1]; // 和左儿子共左端点
38 rbd[rt] = rbd[rt << 1 | 1]; //和右儿子共右端点
39 len[rt] = len[rt << 1] + len[rt << 1 | 1];
40 numseg[rt] = numseg[rt << 1] + numseg[rt << 1 | 1];
41 if (lbd[rt << 1 | 1] && rbd[rt << 1]) numseg[rt] -= 2;//如果左子的右端点和右子的左端点都被覆盖了即两条线重合
42 }
43 }
44 void update(int L, int R, int c, int l, int r, int rt)
45 {
46 if (L <= l && r <= R)
47 {
48 cnt[rt] += c;
49 PushUP(rt, l, r);
50 return;
51 }
52 int m = (l + r) >> 1;
53 if (L <= m) update(L, R, c, lson);
54 if (m < R) update(L, R, c, rson);
55 PushUP(rt, l, r);
56 }
57 int main()
58 {
59 int n;
60 while (~scanf("%d", &n))
61 {
62 int m = 0;
63 int lbd = 10000, rbd = -10000;
64 for (int i = 0; i < n; i++)
65 {
66 int a, b, c, d;
67 scanf("%d%d%d%d", &a, &b, &c, &d);
68 lbd = min(lbd, a);
69 rbd = max(rbd, c);
70 ss[m++] = Seg(a, c, b, 1);
71 ss[m++] = Seg(a, c, d, -1);
72 }
73 sort(ss, ss + m);
74 //数据小可以不离散化
75 int ret = 0, last = 0;
76 for (int i = 0; i < m; i++)
77 {
78 if (ss[i].l < ss[i].r) update(ss[i].l, ss[i].r - 1, ss[i].s, lbd, rbd - 1, 1);
79 ret += numseg[1] * (ss[i + 1].h - ss[i].h);//竖线的长度 = 【下一条即将被扫到的横线的高度 - 现在扫到的横线的高度】*num
80 ret += abs(len[1] - last);//横线的长度 = 【现在这次总区间被覆盖的程度和上一次总区间被覆盖的长度之差的绝对值】
81 last = len[1];//每次都要更新上一次总区间覆盖的长度
82 }
83 printf("%d\n", ret);
84 }
85 return 0;
86 }
hdu1828 Picture(线段树+扫描线+矩形周长)的更多相关文章
- hdu 1828 Picture(线段树扫描线矩形周长并)
线段树扫描线矩形周长并 #include <iostream> #include <cstdio> #include <algorithm> #include &l ...
- HDU 1828 / POJ 1177 Picture --线段树求矩形周长并
题意:给n个矩形,求矩形周长并 解法:跟求矩形面积并差不多,不过线段树节点记录的为: len: 此区间线段长度 cover: 此区间是否被整个覆盖 lmark,rmark: 此区间左右端点是否被覆盖 ...
- HDU1828 Picture 线段树+扫描线模板题
Picture Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Sub ...
- HDU 1264 Counting Squares (线段树-扫描线-矩形面积并)
版权声明:欢迎关注我的博客.本文为博主[炒饭君]原创文章,未经博主同意不得转载 https://blog.csdn.net/a1061747415/article/details/25471349 P ...
- 【hdu1828/poj1177】线段树求矩形周长并
题意如图 题解:这题非常类似与矩形面积并,也是维护一个被覆盖了一次以上的线段总长. 但是周长要算新出现的,所以每次都要和上一次做差求绝对值. x轴做一遍,y轴做一遍. 但是有个问题:矩形边界重合的时候 ...
- POJ 1177 Picture(线段树 扫描线 离散化 求矩形并面积)
题目原网址:http://poj.org/problem?id=1177 题目中文翻译: 解题思路: 总体思路: 1.沿X轴离散化建树 2.按Y值从小到大排序平行与X轴的边,然后顺序处理 如果遇到矩形 ...
- ZOJ 3597 Hit the Target! (线段树扫描线 -- 矩形所能覆盖的最多的点数)
ZOJ 3597 题意是说有n把枪,有m个靶子,每把枪只有一发子弹(也就是说一把枪最多只能打一个靶子), 告诉你第 i 把枪可以打到第j个靶, 现在等概率的出现一个连续的P把枪,在知道这P把枪之后,你 ...
- poj 3277 City Horizon (线段树 扫描线 矩形面积并)
题目链接 题意: 给一些矩形,给出长和高,其中长是用区间的形式给出的,有些区间有重叠,最后求所有矩形的面积. 分析: 给的区间的范围很大,所以需要离散化,还需要把y坐标去重,不过我试了一下不去重 也不 ...
- poj 1177 --- Picture(线段树+扫描线 求矩形并的周长)
题目链接 Description A number of rectangular posters, photographs and other pictures of the same shape a ...
随机推荐
- 基于 OpenMP 的奇偶排序算法的实现
代码: #include <omp.h> #include <iostream> #include <cstdlib> #include <ctime> ...
- 【Problem】前端项目运行:Module build failed:Error Node Sass does not yet support my current environmen
我在运行renren-fast-vue前端项目时,安装完依赖cnpm install 启动服务npm run dev 出现问题. Module build failed: Error: Node Sa ...
- xtrabackup 备份与恢复
书上摘抄 ---深入浅出mysql 448页 grant reload on *.* to 'backup'@'localhost' identified by '123456'; grant re ...
- 【JAVA并发第三篇】线程间通信
线程间的通信 JVM在运行时会将自己管理的内存区域,划分为不同的数据区,称为运行时数据区.每个线程都有自己私有的内存空间,如下图示: Java线程按照自己虚拟机栈中的方法代码一步一步的执行下去,在这一 ...
- 云原生流水线 Argo Workflow 的安装、使用以及个人体验
注意:这篇文章并不是一篇入门教程,学习 Argo Workflow 请移步官方文档 Argo Documentation Argo Workflow 是一个云原生工作流引擎,专注于编排并行任务.它的特 ...
- 实现一个List集合中的某个元素的求和
List<User> userlist = userService.findAll();Integer sum= userlist .stream().collect(Collectors ...
- 长连接 短连接 RST报文
https://baike.baidu.com/item/短连接 短连接(short connnection)是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时,才去建立一个连接,数 ...
- WireShark 之 text2pcap
前言 本来想用 010Editer 的,看到破解教程头都大了,那么就用 WireShark 的 Text2pcap 吧! 正文 打开CMD控制台窗口,转到WireShark安装目录 ,此处可以shif ...
- 如何在opencv下使用SIFT
SIFT即尺度不变特征变换,是用于图像处理领域的一种描述.这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子.SIFT的尺度不变特征变换在图像特征点匹配中十分关键,因为我们从不同角度 ...
- java定时任务之Qutaz
前不久一直在学习Qztaz,干好写了一个案例分享一下给大家,希望大家可以用得到. 那么现在开始吧, 一:什么事Qutaz? Quartz是OpenSymphony开源组织在Job scheduling ...