【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):石头剪刀布小游戏
从今天起开始捣鼓小程序了2018-12-17 10:02:15 跟着教程做了第一个入门实例有兴趣的朋友可以看看: 猜拳游戏布局 程序达到的效果 猜拳游戏的布局是纵向显示了三个组件:文本组件(tex ...
- java-反射和代理
1.类的编译和运行简易过程: java的源码文件(也称为编译单元,以.java为后缀的文件) ↓ 文件内最多只能有一个public修饰的类,否则编译器报错:某个类被public修饰,该类名必需与文件名 ...
- js获取url参数方法
function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&] ...
- 笔记-scrapy-Request/Response
笔记-scrapy-Request/Response 1. 简介 Scrapy使用Request和Response来爬取网站. 2. request class scrapy.http ...
- 使wlr写cnblog的博客-2 设置cnblog帐号
ref:http://www.cnblogs.com/liuxianan/archive/2013/04/13/3018732.html 使用: 打开Windows Live Writer,第一次 ...
- Fragment Touch事件泄露
当Fragment的栈里面有几个fragment的时候,这个时候如果是几个fragment状态是hide,当你触摸当前fragment的时候,下层的fragment的事件被触发,这是由于Touch事件 ...
- java程序——凯撒加密
古罗马皇帝凯撒在打仗时曾经使用过以下方法加密军事情报: 请编写一个程序,使用上述算法加密或解密用户输入的英文字串要求设计思想.程序流程图.源代码.结果截图. 设计思想:输入一个字符串,然后将其中每 ...
- android 事件拦截 (Viewpager不可以左右滑动)
以前没有做过真正的需求,所以从来没有觉得事件拦截分发处理有什么好懂的. 现在做需求了,真的是什么需求都有,你作为开发都要去研究实现.比如说,只能点不能滑动的viewpager.其实这都可以不用view ...
- springboot 采用HttpClient获取天气预报 异常及原因
采用httpClient调用天气预报地址获取出现异常 2018-10-04 15:18:25.815 ERROR 10868 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[ ...
- myeclipse中项目名有红叉,但项目中文件没有报错的解决办法
导入了别人的项目,各种jar包都放好后,path也都build好了,项目也能正常启动,但是就是项目名有红叉,这是为什么呢? 网上有人说Java build path中的jar包missing了,这是一 ...