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

解题方法:
我们用一个栈来维护直线间的关系。首先我们按照直线的斜率从小到大进行排序,斜率一样的按照截距从大
到小进行排序,那么我们可以发现对于斜率相同的一组直线,只有截距最大的那个是有可能被看到的,所以我们
先删掉这些不可能的直线。对于当前的直线,我们将它与栈顶直线的交点设为 A,与栈顶的下一条直线的交点设
为 B。我们发现如果 A 的横坐标大于等于 B 的横坐标,那么栈顶直线必被遮盖,所以弹出直到栈顶直线不被遮盖
后,把当前直线入栈,那么最后栈中的所有直线都是可见直线。
交点坐标直接带入直线方程求解即可。

代码如下:

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
struct Point{
    double a, b;
    int id;
    void read(){
        scanf("%lf%lf", &a, &b);
    }
    bool operator <(const Point &rhs) const{
        if(fabs(a - rhs.a) < eps) return b < rhs.b;
        return a < rhs.a;
    }
}line[50010], stk[50010];
bool ans[50010];
int n, top;
double cross(Point x, Point y){
    return (y.b - x.b) / (x.a - y.a);
}
void ins(Point x){
    while(top){
        if(fabs(stk[top].a - x.a) < eps) top--;
        else if(top > 1 && cross(x, stk[top-1]) <= cross(stk[top], stk[top-1])) top--;
        else break;
    }
    stk[++top] = x;
}

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        line[i].read();
        line[i].id = i;
    }
    sort(line+1, line+n+1);
    for(int i = 1; i <= n; i++) ins(line[i]);
    for(int i = 1; i <= top; i++) ans[stk[i].id] = i;
    for(int i = 1; i <= n; i++){
        if(ans[i]){
            printf("%d ", i);
        }
    }
    printf("\n");
    return 0;
}