bzoj1007[HNOI2008]水平可见直线
传送门
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
-1 0
1 0
0 0
Sample Output
1 2
题解
这道题的想法还算比较清晰。首先将每条边按照斜率排序,用一个栈维护。每一次如果栈顶直线平行于新的直线或者新的直线和栈中第二条直线的交点在栈顶直线和栈中第二条直线的交点的左边,则弹出栈顶元素,因为新的直线的斜率一定比栈顶直线的斜率大,新的交点若在旧的交点的左边,则栈顶的直线显露出来的部分一定会被新的直线完全覆盖,因此可以这么做。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #define eps 1e-8 8 using namespace std; 9 struct node{ 10 int xu; 11 double a,b; 12 }li[50010],st[50010]; 13 bool ans[50010]; 14 int top=0,n; 15 bool cmp(node a,node b){ 16 if(fabs(a.a-b.a)<eps) return a.b<b.b; 17 return a.a<b.a; 18 } 19 double jiao(node a,node b){return (b.b-a.b)/(a.a-b.a);} 20 void add(node a){ 21 while(top){ 22 if(fabs(st[top].a-a.a)<eps) top--; 23 else{ 24 if(top>1 && jiao(a,st[top-1])<=jiao(st[top],st[top-1])) top--; 25 else break ; 26 } 27 } 28 st[++top]=a; 29 } 30 int main(){ 31 scanf("%d",&n); 32 int i,j; 33 for(i=1;i<=n;++i){ 34 scanf("%lf%lf",&li[i].a,&li[i].b); 35 li[i].xu=i; 36 } 37 sort(li+1,li+1+n,cmp); 38 for(i=1;i<=n;++i){ 39 add(li[i]); 40 } 41 for(i=1;i<=top;++i){ 42 ans[st[i].xu]=1; 43 } 44 for(i=1;i<=n;++i){ 45 if(ans[i]) printf("%d ",i); 46 } 47 return 0; 48 }