1. 程式人生 > >【BZOJ1007】【HNOI2008】水平可見直線 幾何 單調棧

【BZOJ1007】【HNOI2008】水平可見直線 幾何 單調棧

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】水平可見直線 幾何 單調棧