题目描述

地平线(x轴)上有n个矩(lou)形(fang),用三个整数h[i],l[i],r[i]来表示第i个矩形:矩形左下角为(l[i],0),右上角为(r[i],h[i])。地平线高度为0。在轮廓线长度最小的前提下,从左到右输出轮廓线。

下图为样例2。

输入输出格式

输入格式:

第一行一个整数n,表示矩形个数

以下n行,每行3个整数h[i],l[i],r[i]表示第i个矩形。

输出格式:

第一行一个整数m,表示节点个数

以下m行,每行一个坐标表示轮廓线上的节点。从左到右遍历轮廓线并顺序输出节点。第一个和最后一个节点的y坐标必然为0。

输入输出样例

输入样例#1:

2
3 0 2
4 1 3
输出样例#1:

6
0 0
0 3
1 3
1 4
3 4
3 0
输入样例#2:

5
3 -3 0
2 -1 1
4 2 4
2 3 7
3 6 8
输出样例#2:

14
-3 0
-3 3
0 3
0 2
1 2
1 0
2 0
2 4
4 4
4 2
6 2
6 3
8 3
8 0

说明

【数据范围】

对于30%的数据,n<=100

对于另外30%的数据,n<=100000,1<=h[i],l[i],r[i]<=1000

对于100%的数据,1<=n<=100000,1<=h[i]<=10^9,-10^9<=l[i]<r[i]<=10^9

先留一点思考的时间

题解:

题意就是给出一张图形的坐标 ,求出这张图中的拐点个数和坐标.

对于60%的数据,可以直接模拟加离散化. 用数组存下每一个X轴上的点的最高值,但是为什么要用离散化呢?

当遇到这样的情况时,代码就会出问题:

h[2]=3, h[3]=2,那么在循环扫过来时就不会处理(2,0)和(3,0),但这两个点是存在的,所以要用离散化,将一格坐标变为两格坐标:

这样的话循环只会多一点常数,但是答案能保证正确.

对于100%的数据:

可以用离散化加线段树扫描线.

离散化加线段树的方法与暴力相类似,用线段树加速了区间最值的修改与查询.

扫描线

将矩形的左右两边看作是一条线段,分别是一个矩形的入边和出边,然后通过排序确定遍历扫描线的顺序.这样就可以在O(nlogn)的时间复杂度内完成.

那么扫描线应该怎么排序呢?

我们用扫描线就是为了确定一个矩形的覆盖情况,然后找到图中的拐点.那么首先就应该是要按照X轴上的坐标进行排序.并且应该先扫入边,再扫出边,这样可以保证不会有什么鬼的奇怪的数据有左右边相同的矩形使得结果出问题. 在判断完前两种情况后,现在正在排序的两条线就同属于一种边(同为入边或出边),那么若是入边,则越高越容易挡住另一个矩形,就要放在前面才会使得更新一次答案时没有矩形被挡住.若是出边,则越低越容易被挡住,则要放在后面,这样就可以使得高的出边在后面被扫到,就可以保证在删除最后一条出边时更新答案没有矩形被挡住.

 bool cmpl(line a,line b){
if(a.x!=b.x) return a.x<b.x;
if(a.f!=b.f) return a.f<b.f;
if(a.f==) return a.h>b.h;
if(a.f==) return a.h<b.h;
}

然后在后面扫描的时候就直接用for循环遍历每一条扫描线.那么扫到一条扫描线就有两种情况:

  1. 属于入边,如果比当前最高值还要高,则出现了拐点,更新答案.
  2. 属于出边,如果高度为当前最大高度且该高度出边只有一条,则出现了拐点,更新答案.

这里记录当前已经加入扫描的矩形时用了一个STL容器:multiset.这个东西与set不同的地方体现在它可以存入相同的元素,且默认单调递增,那么对于这个题目的实现就提供了一个很好的帮助.

C++ STL MultiSet类成员函数列表如下:

begin() 返回指向第一个元素的迭代器

clear() 清除所有元素

count() 返回指向某个值元素的个数

empty() 如果集合为空,返回true

end() 返回指向最后一个元素的迭代器

equal_range() 返回集合中与给定值相等的上下限的两个迭代器

erase() 删除集合中的元素

find() 返回一个指向被查找到元素的迭代器

get_allocator() 返回多元集合的分配器

insert() 在集合中插入元素

key_comp() 返回一个用于元素间值比较的函数

lower_bound() 返回指向大于(或等于)某值的第一个元素的迭代器

max_size() 返回集合能容纳的元素的最大限值

rbegin() 返回指向多元集合中最后一个元素的反向迭代器

rend() 返回指向多元集合中第一个元素的反向迭代器

size() 多元集合中元素的数目

swap() 交换两个多元集合变量

upper_bound() 返回一个大于某个值元素的迭代器

value_comp() 返回一个用于比较元素间的值的函数

然后把答案存到数组里,根据题目要求输出.

下面是代码:

#include<bits/stdc++.h>
using namespace std;
const int N=; int n;
int cnt=;
int cnta=; struct line{
int x,h,f;
}l[N*]; struct answer{
int x,y;
}ans[N*]; int gi(){
int ans=,f=;char i=getchar();
while(i<''||i>''){if(i=='-')f=-;i=getchar();}
while(i>=''&&i<=''){ans=ans*+i-'';i=getchar();}
return ans*f;
} bool cmpl(line a,line b){
if(a.x!=b.x) return a.x<b.x;
if(a.f!=b.f) return a.f<b.f;
if(a.f==) return a.h>b.h;
if(a.f==) return a.h<b.h;
} int main(){
//freopen("Fort.in","r",stdin);
//freopen("Fort.out","w",stdout);
n=gi();
for(int i=,x,y,z;i<=n;i++){
x=gi(); y=gi(); z=gi();
l[++cnt].x=y; l[cnt].h=x; l[cnt].f=;
l[++cnt].x=z; l[cnt].h=x; l[cnt].f=;
}
sort(l+,l+cnt+,cmpl);
multiset <int> s; s.insert();
for(int i=;i<=cnt;i++){
int maxh=*s.rbegin();
if(l[i].f==){
if(l[i].h>maxh){
ans[++cnta].x=l[i].x; ans[cnta].y=maxh;
ans[++cnta].x=l[i].x; ans[cnta].y=l[i].h;
}
s.insert(l[i].h);
}
if(l[i].f==){
if(l[i].h==maxh&&s.count(maxh)==){
s.erase(maxh);
ans[++cnta].x=l[i].x; ans[cnta].y=l[i].h;
ans[++cnta].x=l[i].x; ans[cnta].y=*s.rbegin();
}
else s.erase(s.find(l[i].h));
}
}
cout<<cnta<<endl;
for(int i=;i<=cnta;i++)
printf("%d %d\n",ans[i].x,ans[i].y);
return ;
}

[洛谷P1382] 楼房的更多相关文章

  1. 洛谷P4198 楼房重建 (分块)

    洛谷P4198 楼房重建 题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题, ...

  2. 洛谷 P4198 楼房重建 题解

    题面 首先你要知道题问的是什么:使用一种数据结构,动态地维护以1为起点地最长上升子序列(把楼房的高度转化成斜率地序列)的长度: 怎么做?线段树! 我们在线段树上维护两个东西:1.这个区间内斜率的最大值 ...

  3. 洛谷P4198 楼房重建 单调栈+线段树

    正解:单调栈+线段树 解题报告: 传送门! 首先考虑不修改的话就是个单调栈板子题昂,这个就是 然后这题的话,,,我怎么记得之前考试好像有次考到了类似的题目昂,,,?反正我总觉着这方法似曾相识的样子,, ...

  4. 洛谷P4198 楼房重建(线段树)

    题意 题目链接 Sol 别问我为什么发两遍 就是为了骗访问量 这个题的线段树做法,,妙的很 首先一个显然的结论:位置\(i\)能被看到当且仅当\(\frac{H_k}{k} < \frac{H_ ...

  5. 洛谷P4198 楼房重建

    题意:给定序列,每次修改一个值,求前缀最大值的个数. 解:线段树经典应用. 每个节点维护最大值和该区间前缀最大值个数. 发现我们不用下传标记,只需要合并区间. 需要实现一个函数int ask([l r ...

  6. 洛谷 P4198 楼房重建

    思路 此题可转化为以下模型 给定序列\(a[1...n]\),支持单点修改,每次求区间单调栈大小 \(n,Q\le 10^5\) 区间单调栈是什么呢?对于一个区间,建立一个栈,首先将第一个元素入栈,从 ...

  7. 楼房 洛谷1382 && codevs2995

    P1382 楼房 题目描述 地平线(x轴)上有n个矩(lou)形(fang),用三个整数h[i],l[i],r[i]来表示第i个矩形:矩形左下角为(l[i],0),右上角为(r[i],h[i]).地平 ...

  8. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  9. 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...

随机推荐

  1. android staido 断点遇到的坑

    今天排查数据布点问题,发现sd卡上面的文件莫名消失. 怎么可能?系统不可能删除你的文件,但是我调试,删除文件的代码, 一直都没有执行啊. 后来发现,子线程里面代码,android stadio 可能断 ...

  2. ionic 向路由中的templateUrl(模板页)传值

    .state('product', { url: '/product/:id', templateUrl: function ($routeParams) { return '/Product/Ind ...

  3. Qt 个性化标题栏,自定义标题栏

    目前还没有达到自己满意的地步,魔方别人写的的,先提供参考,后面在加入新的东西 头文件 #ifndef TITLEBAR_H #define TITLEBAR_H #include <QWidge ...

  4. Java并发基础--线程安全

    一.线程安全 1.线程安全的概念 线程安全:某个类被单个线程,或者多个线程同时访问,所表现出来的行为是一致,则可以说这个类是线程安全的. 2.什么情况下会出现线程安全问题 在单线程中不会出现线程安全问 ...

  5. C++类数组批量赋值

    类和结构体不同,结构体在初始化时可以使用{...}的方法全部赋值,但是结构体怎么办呢?一种是把数据数组写到一个相同的结构体内,然后for循环使用一个非构造函数写入到类数组中.另一种方法是直接写入到对应 ...

  6. [Node] Agenda 中文文档 定时任务调度系统[基础篇]

    Agenda简介 使用步骤概述 步骤详述 初始化 定义任务 参数说明: 配置任务 参数说明 设置监听 注意事项 Agenda简介 Agenda是一个定时任务管理模块,它将node中的定时任务存储在数据 ...

  7. Tensorflow Serving介绍及部署安装

    TensorFlow Serving 是一个用于机器学习模型 serving 的高性能开源库.它可以将训练好的机器学习模型部署到线上,使用 gRPC 作为接口接受外部调用.更加让人眼前一亮的是,它支持 ...

  8. 用tensorflow实现自然语言处理——基于循环神经网络的神经语言模型

    自然语言处理和图像处理不同,作为人类抽象出来的高级表达形式,它和图像.声音不同,图像和声音十分直觉,比如图像的像素的颜色表达可以直接量化成数字输入到神经网络中,当然如果是经过压缩的格式jpeg等必须还 ...

  9. 以太坊solidity编程常见错误(不定期更新)

    1.报错: Expected token Semicolon got 'eth_compileSolidity' funtion setFunder(uint _u,uint _amount){ 解决 ...

  10. iOS银联,支付宝,微信,ping++开发文档

    银联支付 银联支付目测只需两个参数 1.tn 其实就是订单号 2.mode 是测试环境还是线上环境 开发步骤 1.首先客户端浏览商品,点击下单,请求到达商户后台 2.商户后台在提交订单信息到银联后台 ...