BZOJ 1007: [HNOI2008]水平可见直线
1007: [HNOI2008]水平可见直线
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 7769 Solved: 2973
[Submit][Status][Discuss]
Description
在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条直线两两不重合.求出所有可见的直线.
Input
第一行为N(0 < N < 50000),接下来的N行输入Ai,Bi
Output
从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格
Sample Input
3
-1 0
1 0
0 0
Sample Output
1 2
题解
按斜率排序后,从小到大枚举,用栈维护当前可见的直线,如果当前考虑的直线和栈顶直线的交点在栈顶直线与栈顶第二条直线的交点的左侧,那么栈顶直线一定看不见,弹出。
因为要用栈顶第二条直线,用手写栈即可。
注意处理精度问题。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> using namespace std; const int N=500005; const double eps=1e-8; int n,top; struct line{ int id; double a,b; }l[N],st[N]; bool cmp1(line a,line b){ if(fabs(a.a-b.a)<=eps)return a.b<b.b; return a.a<b.a; } bool cmp2(line a,line b){ return a.id<b.id; } double cross(line a,line b){ return (b.b-a.b)/(a.a-b.a); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%lf%lf",&l[i].a,&l[i].b); l[i].id=i; } sort(l+1,l+n+1,cmp1); for(int i=1;i<=n;i++){ while(top){ if(fabs(l[i].a-st[top].a)<=eps)top--; else if(top>1&&cross(l[i],st[top-1])<=cross(st[top],st[top-1]))top--; else break; } st[++top]=l[i]; } sort(st+1,st+top+1,cmp2); for(int i=1;i<=top;i++){ printf("%d ",st[i].id); } printf("\n"); return 0; }