【bzoj1007】[HNOI2008]水平可见直线 半平面交/单调栈
题目描述
在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的.
例如,对于直线:
L1:y=x; L2:y=-x; L3:y=0
则L1和L2是可见的,L3是被覆盖的.
给出n条直线,表示成y=Ax+B的形式(|A|,|B|<=500000),且n条直线两两不重合.求出所有可见的直线.
输入
第一行为N(0 < N < 50000),接下来的N行输入Ai,Bi
输出
从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格
样例输入
3
-1 0
1 0
0 0
样例输出
1 2
题解
半平面交/单调栈
其实一开始学半平面交就是为了这道题,于是码了一发。其中需要在最上方添加一条辅助直线(辅助半平面)以使得半平面交为封闭图形。
代码:
- #include <cmath>
- #include <cstdio>
- #include <algorithm>
- #define N 50010
- using namespace std;
- struct point
- {
- double x , y;
- point() {}
- point(double a , double b) {x = a , y = b;}
- point operator+(const point &a)const {return point(x + a.x , y + a.y);}
- point operator-(const point &a)const {return point(x - a.x , y - a.y);}
- point operator*(const double &a)const {return point(a * x , a * y);}
- }p[N];
- struct line
- {
- point p , v;
- double ang;
- int id;
- }a[N] , q[N];
- inline double cross(point a , point b) {return a.x * b.y - a.y * b.x;}
- inline bool left(line a , point b) {return cross(a.v , b - a.p) >= 0;}
- inline point inter(line a , line b)
- {
- point u = a.p - b.p;
- double tmp = cross(b.v , u) / cross(a.v , b.v);
- return a.p + a.v * tmp;
- }
- bool cmp1(const line &a , const line &b)
- {
- return a.ang == b.ang ? left(a , b.p) : a.ang < b.ang;
- }
- bool cmp2(const line &a , const line &b)
- {
- return a.id < b.id;
- }
- int main()
- {
- int n , i , tot = 1 , l = 1 , r = 1;
- double k , b;
- scanf("%d" , &n);
- for(i = 1 ; i <= n ; i ++ ) scanf("%lf%lf" , &k , &b) , a[i].p = point(0 , b) , a[i].v = point(-1 , -k);
- a[n + 1].p = point(0 , 1e18) , a[n + 1].v = point(1 , 0);
- a[n + 2].p = point(-3e9 , 0) , a[n + 2].v = point(0 , 1);
- a[n + 3].p = point(3e9 , 0) , a[n + 3].v = point(0 , -1);
- for(i = 1 ; i <= n + 3 ; i ++ ) a[i].ang = atan2(a[i].v.y , a[i].v.x) , a[i].id = i;
- sort(a + 1 , a + n + 4 , cmp1);
- for(i = 2 ; i <= n + 3 ; i ++ )
- if(a[i].ang != a[i - 1].ang)
- a[++tot] = a[i];
- q[1] = a[1];
- for(i = 2 ; i <= tot ; i ++ )
- {
- while(l < r && left(a[i] , p[r - 1])) r -- ;
- while(l < r && left(a[i] , p[l])) l ++ ;
- q[++r] = a[i];
- if(l < r) p[r - 1] = inter(q[r - 1] , q[r]);
- }
- while(l < r && left(q[l] , p[r - 1])) r -- ;
- sort(q + l , q + r + 1 , cmp2);
- for(i = l ; i <= r - 3 ; i ++ ) printf("%d " , q[i].id);
- return 0;
- }
然后就被D了一顿= =
事实上,本题并不需要复杂的码农半平面交= =
考虑将所有点按照斜率从小到大排序,去重,那么就可以直接把所有直线压入栈中,不满足的弹出。
具体地,如果栈顶直线被当前直线和次栈顶直线完全覆盖,那么就把栈顶弹出。这样维护一个单调栈即可。
如何判断站定直线被覆盖?两种方法:判断栈顶与次栈顶直线的交点是否在当前直线下方(半平面交的写法),或判断栈顶与次栈顶直线的交点是否在当前直线与栈顶直线的交点的右侧(单调栈的写法)。
两种写法均可行,代码中写了第二种,稍微会短一些。
- #include <cstdio>
- #include <algorithm>
- #define N 50010
- using namespace std;
- struct data
- {
- double k , b;
- int id;
- bool operator<(const data &a)const {return k == a.k ? b > a.b : k < a.k;}
- }a[N] , s[N];
- int ans[N];
- inline double inter(data a , data c)
- {
- return (a.b - c.b) / (c.k - a.k);
- }
- int main()
- {
- int n , i , tot = 1 , top = 1;
- scanf("%d" , &n);
- for(i = 1 ; i <= n ; i ++ ) scanf("%lf%lf" , &a[i].k , &a[i].b) , a[i].id = i;
- sort(a + 1 , a + n + 1);
- for(i = 2 ; i <= n ; i ++ )
- if(a[i].k != a[i - 1].k)
- a[++tot] = a[i];
- s[1] = a[1];
- for(i = 2 ; i <= tot ; i ++ )
- {
- while(top > 1 && inter(s[top - 1] , s[top]) >= inter(s[top] , a[i])) top -- ;
- s[++top] = a[i];
- }
- for(i = 1 ; i <= top ; i ++ ) ans[s[i].id] = 1;
- for(i = 1 ; i <= n ; i ++ )
- if(ans[i])
- printf("%d " , i);
- return 0;
- }
【bzoj1007】[HNOI2008]水平可见直线 半平面交/单调栈的更多相关文章
- [BZOJ1007](HNOI2008)水平可见直线(半平面交习题)
Description 在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的. 例如,对于直线: ...
- [日常摸鱼]bzoj1007[HNOI2008]水平可见直线-半平面交(对偶转凸包)
不会写半平面交-然后发现可以转成对偶凸包问题 具体见这里:http://trinkle.blog.uoj.ac/blog/235 相关的原理我好像还是不太懂-orz #include<cstdi ...
- BZOJ.1007.[HNOI2008]水平可见直线(凸壳 单调栈)
题目链接 可以看出我们是要维护一个下凸壳. 先对斜率从小到大排序.斜率最大.最小的直线是一定会保留的,因为这是凸壳最边上的两段. 维护一个单调栈,栈中为当前可见直线(按照斜率排序). 当加入一条直线l ...
- BZOJ 1007 [HNOI2008]水平可见直线 ——半平面交 凸包
发现需要求一个下凸的半平面上有几个交点. 然后我们把它变成凸包的问题. 好写.好调.还没有精度误差. #include <map> #include <ctime> #incl ...
- [bzoj1007][HNOI2008]水平可见直线_单调栈
水平可见直线 bzoj-1007 HNOI-2008 题目大意:给你n条直线,为你从上往下看能看见多少跳直线. 注释:能看见一条直线,当且仅当这条直线上存在一条长度>0的线段使得这条线段上方没有 ...
- BZOJ1007: [HNOI2008]水平可见直线(单调栈)
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 8638 Solved: 3327[Submit][Status][Discuss] Descripti ...
- bzoj1007: [HNOI2008]水平可见直线 单调栈维护凸壳
在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的.例如,对于直线:L1:y=x; L2:y=-x; L3 ...
- bzoj1007 [HNOI2008]水平可见直线——单调栈
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1007 可以把直线按斜率从小到大排序,用单调栈维护,判断新直线与栈顶的交点和栈顶与它之前直线的 ...
- bzoj1007[HNOI2008]水平可见直线
cycleke神说要用半平面交(其实他也用的凸包),把我吓了一跳,后来发现(看题解)其实可以先按斜率排序,再将最小的两条线入栈,如果其与栈顶元素的交点在上一个点的左边,则将栈顶元素出栈.这是一个开口向 ...
随机推荐
- Selenium页面加载策略
https://blog.csdn.net/wkb342814892/article/details/81611737 https://blog.csdn.net/ouyanggengcheng/ar ...
- Centos7上搭建activemq集群和zookeeper集群
Zookeeper集群的搭建 1.环境准备 Zookeeper版本:3.4.10. 三台服务器: IP 端口 通信端口 10.233.17.6 2181 2888,3888 10.233.17.7 2 ...
- java实现 zip解压缩
程序实现了ZIP压缩.共分为2部分 : 压缩(compression)与解压(decompression) 大致功能包括用了多态,递归等JAVA核心技术,可以对单个文件和任意级联文件夹进行压缩和解压. ...
- C6748的GPIO口配置使用
2018年1月17日更新: 这几天用了创龙的C6748的库,对于GPIO配置十分不爽,我移植了RK6748的库,用起来十分酸爽,把下面的文件加入到工程中,然后include头文件后就可以使用.非常好使 ...
- Leetcode 701. 二叉搜索树中的插入操作
题目链接 https://leetcode.com/problems/insert-into-a-binary-search-tree/description/ 题目描述 给定二叉搜索树(BST)的根 ...
- 笔记-scrapy-setting
笔记-scrapy-setting 1. 简介 Scrapy设置允许您自定义所有Scrapy组件的行为,包括核心,扩展,管道和蜘蛛本身. 可以使用不同的机制来填充设置,每种机制都有不同的优先级 ...
- storm实时计算实例(socket实时接入)
介绍 实现了一个简单的从实时日志文件监听,写入socket服务器,再接入Storm计算的一个流程. 源码 日志监听实时写入socket服务器 package socket; import java ...
- Servlet过滤器---编码转换过滤器
该实例用于将请求与相应的编码设置为当前网站的默认编码 java类: import java.io.IOException; import javax.servlet.Filter; import ja ...
- 大数据服务大比拼:AWS VS. AzureVS.谷歌
[TechTarget中国原创] 对于企业用户来说,大数据服务是一项较具吸引力的云服务.三大巨头AWS.Azure以及谷歌都在力争夺得头把交椅,但是最后到底是哪一家能够取得王座之战的胜利呢? 云市场正 ...
- centso下如何解压RAR文件
tar -xvf rarlinux-3.9.3.tar.gz cd rar make 看见下面这些信息就是安装成功了 mkdir -p /usr/local/bin mkdir -p /usr/l ...