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

题目大意

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

  (1leq nleq 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;
}
原文地址:https://www.cnblogs.com/ywwyww/p/8510701.html