[关键字]:数学
[题目大意]:给出n条直线的k和b(y=kx+b),求出从y轴无限高的地方向下看能看到哪几条直线。
//====================================================================================
[分析]:因为斜率最小和斜率最大的两条边一定是可以看见的,所以将斜率从小到大排序依次加入一个记录所有可见直线的凸壳中的堆栈。记录每条边的和凸壳左交点的x值(因为直线是无穷长的可以规定一个区间-1e100到1e100使其变成有限的),而右交点是不用记录的因为右交点=下一条直线的左交点。因为是按斜率从小到大的顺序加入,所以如果当前加入的直线和栈顶的直线的交点x值小于栈顶直线在凸壳中的左交点x值则可弹栈,直到不成立在将这条直线入栈。而这个栈一定是按x从小到大排序的,所以可以利用二分查找降低复杂度。
[代码]:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=60000;
struct node
{
double k,b;
int id;
}a[MAXN];
int n;
double l[MAXN],qq;
int d[MAXN];
bool b[MAXN];
bool cmp(node a,node b)
{
if (a.k<b.k || (a.k==b.k && a.b>b.b)) return 1; else return 0;
}
double Cross(node a,node b)
{
if (a.k==b.k) return 1e100;
else return (b.b-a.b)/(a.k-b.k);
}
int Find(int left,int right,int x)
{
int mid,ans;
while (left<=right)
{
mid=(left+right)/2;
qq=Cross(a[x],a[d[mid]]);
if (qq<=l[mid]) right=mid-1; else ans=mid,left=mid+1;
}
qq=Cross(a[x],a[d[ans]]);
return ans;
}
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%lf%lf",&a[i].k,&a[i].b),a[i].id=i;
sort(a+1,a+n+1,cmp);
l[1]=-1e100;
d[1]=1;
int k=1;
for (int i=2;i<=n;++i)
{
int j=Find(1,k,i);
//if (qq<l[j]) continue;
k=j+1;
d[k]=i;
l[k]=qq;
}
memset(b,0,sizeof(b));
for (int i=1;i<=k;++i)
b[a[d[i]].id]=1;
for (int i=1;i<=n;++i)
if (b[i]) printf("%d\n",i);
return 0;
}