1007: [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

Sample Output

1 2
 
--------------------------------------我是努力的分割线----------------------------------------
把所有的直线按照斜率从小到大排序。
首先,斜率相同的直线只保留截距最大的。
所有直线依次入栈,如果当前待入栈的直线与栈顶直线的交点在栈顶的两条直线的交点的左侧,则栈顶的直线一定被“全覆盖”,即可弹出栈顶直线,最后把待入栈直线压栈。
这个性质根据可以根据凸包推导得出,当然在草稿纸上随手画几笔也可轻易得出。
在比较交点的横坐标大小时,因为直线已按斜率排序, 可以把除法转为乘法,免去精度处理。
/**************************************************************
    Problem: 1007
    User: xialan
    Language: C++
    Result: Accepted
    Time:284 ms
    Memory:2516 kb
****************************************************************/
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<cmath>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
typedef long long LL;
const int M=50010;
struct line{LL x,y;int f;}a[M];
bool cmp(line p,line q){return p.x<q.x||p.x==q.x&&p.y>q.y;}
int stk[M];
bool ans[M]={0};
int main(){
    int n;
    scanf("%d",&n);
    rep(i,1,n){scanf("%lld%lld",&a[i].x,&a[i].y);a[i].f=i;}
    sort(a+1,a+n+1,cmp);
    int h=0;
    rep(i,1,n){
        if(!h)stk[++h]=i;
        else if(a[i].x==a[stk[h]].x)continue;
        else{
            while(h>1&&(a[stk[h]].y-a[i].y)*(a[stk[h]].x-a[stk[h-1]].x)<=(a[stk[h-1]].y-a[stk[h]].y)*(a[i].x-a[stk[h]].x))h--;
            stk[++h]=i;
        }
    }
    rep(i,1,h)ans[a[stk[i]].f]=1;
    rep(i,1,n)if(ans[i])printf("%d ",i);
    return 0;
}