题目描述

在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]水平可见直线 半平面交/单调栈的更多相关文章

  1. [BZOJ1007](HNOI2008)水平可见直线(半平面交习题)

    Description 在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的.     例如,对于直线:   ...

  2. [日常摸鱼]bzoj1007[HNOI2008]水平可见直线-半平面交(对偶转凸包)

    不会写半平面交-然后发现可以转成对偶凸包问题 具体见这里:http://trinkle.blog.uoj.ac/blog/235 相关的原理我好像还是不太懂-orz #include<cstdi ...

  3. BZOJ.1007.[HNOI2008]水平可见直线(凸壳 单调栈)

    题目链接 可以看出我们是要维护一个下凸壳. 先对斜率从小到大排序.斜率最大.最小的直线是一定会保留的,因为这是凸壳最边上的两段. 维护一个单调栈,栈中为当前可见直线(按照斜率排序). 当加入一条直线l ...

  4. BZOJ 1007 [HNOI2008]水平可见直线 ——半平面交 凸包

    发现需要求一个下凸的半平面上有几个交点. 然后我们把它变成凸包的问题. 好写.好调.还没有精度误差. #include <map> #include <ctime> #incl ...

  5. [bzoj1007][HNOI2008]水平可见直线_单调栈

    水平可见直线 bzoj-1007 HNOI-2008 题目大意:给你n条直线,为你从上往下看能看见多少跳直线. 注释:能看见一条直线,当且仅当这条直线上存在一条长度>0的线段使得这条线段上方没有 ...

  6. BZOJ1007: [HNOI2008]水平可见直线(单调栈)

    Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 8638  Solved: 3327[Submit][Status][Discuss] Descripti ...

  7. bzoj1007: [HNOI2008]水平可见直线 单调栈维护凸壳

    在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的.例如,对于直线:L1:y=x; L2:y=-x; L3 ...

  8. bzoj1007 [HNOI2008]水平可见直线——单调栈

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1007 可以把直线按斜率从小到大排序,用单调栈维护,判断新直线与栈顶的交点和栈顶与它之前直线的 ...

  9. bzoj1007[HNOI2008]水平可见直线

    cycleke神说要用半平面交(其实他也用的凸包),把我吓了一跳,后来发现(看题解)其实可以先按斜率排序,再将最小的两条线入栈,如果其与栈顶元素的交点在上一个点的左边,则将栈顶元素出栈.这是一个开口向 ...

随机推荐

  1. ES6初识-函数扩展

    默认值 function test(x,y='world'){ console.log('默认值'); } function test2(...arg){ for(let v of arg){ con ...

  2. 深入浅出:了解jsonp跨域的九种方式

    什么是“”跨域”: 跨域访问,简单来说就是 A 网站的 javascript 代码试图访问 B 网站,包括提交内容和获取内容.由于安全原因,跨域访问是被各大浏览器所默认禁止的.当一个域与其他域建立了信 ...

  3. MySQL5.6基于mysql-proxy实现读写分离

    已经搭建好MySQL主从架构 10.205.22.185 #mysql-proxy 10.205.22.186 #master 10.205.22.187 #slave 1.安装mysql-proxy ...

  4. scala高级特性-01

    目标一:深入理解高阶函数 高阶函数 1.1概念 Scala混合了面向对象和函数式的特性, 我们通常将可以做为参数传递到方法中的表达式叫做函数. 在函数式编程语言中,函数是“头等公民”, 高阶函数包含: ...

  5. Oozie wordcount实战

    一.定义 基本概念 Action: An execution/computation task (Map-Reduce job, Pig job, a shell command). It can a ...

  6. Android 数据库中的数据给到ListView

    前言:因为之前学的都是用一个自己定义的类,完成将某一个bean中的数据直接获取,而实际中通常是通过数据库来得到的,总之,最终就是要得到数据.提一下最重要的东西,我把它叫做代理,如同一个校园代理,没有他 ...

  7. 4.bootstrap的form表单的form-group和form-control的区别与联系

    1. form-group一般用于div form-control一般用于置于div中的标签元素,为了让控件在各种表单风格中样式不出错,需要添加类名“form-control”,如: <form ...

  8. How to send CTRL+BREAK signal to detached command-line process

    1.GenerateConsoleCtrlEvent function Sends a specified signal to a console process group that shares ...

  9. Docker应用设计四大关键

    TechTarget中国原创] Docker已经垄断了容器技术.设计应用时注意考虑便携性能够帮助企业利用容器技术能提供的所有优势. 随着Docker应用和容器越来越流行,很多公司都开始将容器技术作为其 ...

  10. 创建、导入、导出、复制以及粘贴 WMI 筛选器

    TechNet 库 Deployment Forefront Identity and Access Management 基础结构优化 浏览器 Microsoft Dynamics Products ...