【BZOJ1007】【HNOI2008】水平可见直线 几何 单调栈

题目大意

  给你\(n\)条直线\(y=kx+b\),问你从\(y\)值为正无穷大处往下看能看到那些直线。

  \(1\leq n\leq 500000\)

题解

  如果对于两条直线\(l_i,l_j\)\(k_i=k_j\)\(b_i>b_j\),那么\(l_j\)不可能被看见。

  把直线按\(k\)从小到大排序。如果发生了下图的情况(即\(l_1\)\(l_3\)的交点的\(x\)坐标比\(l_2\)\(l_3\)的交点的\(x\)坐标小),则\(l_2\)就不可能被看见。我们可以用栈来维护当前可以看见的直线,如果栈顶那条直线不满足要求,就pop。

  时间复杂度:每个点只会入栈一次,出栈一次,所以时间复杂度是\(O(n)\)的。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
struct line
{
	double k,b;
	int id;
};
line a[500010];
line b[500010];
int cmp(line a,line b)
{
	if(a.k!=b.k)
		return a.k<b.k;
	return a.b<b.b;
}
int q[500010];
int c[500010];
double cross(line a,line b)
{
	return (b.b-a.b)/(a.k-b.k);
}
int main()
{
	int n;
	scanf("%d",&n);
	int i;
	for(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);
	int m=0;
	for(i=1;i<=n;i++)
		if(i==n||a[i].k!=a[i+1].k)
			b[++m]=a[i];
	int t=0;
	for(i=1;i<=m;i++)
	{
		while(t>=2&&cross(b[i],b[q[t-1]])<=cross(b[q[t]],b[q[t-1]])+1e-9)
			t--;
		q[++t]=i;
	}
	for(i=1;i<=t;i++)
		c[i]=b[q[i]].id;
	sort(c+1,c+t+1);
	for(i=1;i<=t;i++)
		printf("%d ",c[i]);
	return 0;
}
posted @ 2018-03-05 18:52  ywwyww  阅读(125)  评论(0编辑  收藏  举报