【BZOJ1007】【HNOI2008】水平可見直線 幾何 單調棧
阿新 • • 發佈:2018-03-05
LG 我們 hnoi2008 復雜度 b- zoj scan noi IT 的。
題目大意
給你\(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;
}
【BZOJ1007】【HNOI2008】水平可見直線 幾何 單調棧