【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神说要用半平面交(其实他也用的凸包),把我吓了一跳,后来发现(看题解)其实可以先按斜率排序,再将最小的两条线入栈,如果其与栈顶元素的交点在上一个点的左边,则将栈顶元素出栈.这是一个开口向 ...
随机推荐
- 1.Netty入门
Netty入门 1.Netty介绍 (1)百度百科介绍: Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络 ...
- 时间复杂度 log n
时间复杂度 O(log n) 意味着什么? 预先知道算法的复杂度是一回事,了解其后的原理是另一件事情. 不管你是计算机科班出身还是想有效解决最优化问题,如果想要用自己的知识解决实际问题,你都必须理解时 ...
- selenium破解极限
一共分为两端段代码: 第一段:获取cookie 第二段:通过cookie登陆 这里以百度云为例: 第一部分:保存cookies,直接在cmd中执行就好 >>> from seleni ...
- SqlServer2008/2005数据库日志收缩
1.SQL2008数据库USE [master]GOALTER DATABASE 数据库名称 SET RECOVERY SIMPLE WITH NO_WAITALTER DATABASE 数据库名称 ...
- linux 安装mysql5.6 yum
安装mysql: 查看mysql: rpm -qa | grep -i mysql 安装必要的环境 yum -y install gcc gcc-c++ ncurses-devel perl 查看环境 ...
- PHP处理mysql事务
MYSQL的事务处理主要有两种方法.1.用begin,rollback,commit来实现begin 开始一个事务rollback 事务回滚commit 事务确认2.直接用set来改变mysql的自动 ...
- python+scrapy环境搭建步骤描述
Python3(3.5.4)搭建爬虫系统步骤描述: 1.下载python安装包,路径:https://www.python.org/downloads/windows/ 选择3.5.4版本64位的安 ...
- caioj:1093: 并查集2(scy的删边问题) C++
题目描述 [题目描述] 读入一个无向图(可能含有多个连通分支),输出最多能删掉多少条边,而不改变这个图任意两点的连通性(原来连通的两个点依然连通,不连通的依然不连通). [输入格式] 第一行为图的顶点 ...
- Mybatis中updateByPrimaryKeySelective和updateByPrimaryKey区别
int updateByPrimaryKeySelective(TbItem record); int updateByPrimaryKey(TbItem record); 上面的是逆转工程生成的Ma ...
- GCJ:2008 Round1AA-Minimum Scalar Product(有序数组倒序乘积和最小)
题目链接:https://code.google.com/codejam/contest/32016/dashboard#s=p0 Minimum Scalar Product This contes ...