1. 程式人生 > >1007: [HNOI2008]水平可見直線[單調棧]

1007: [HNOI2008]水平可見直線[單調棧]

題意:  
在xoy直角座標平面上有n條直線L1,L2,…Ln,若在y值為正無窮大處往下看,能見到Li的某個子線段,則稱Li為
可見的,否則Li為被覆蓋的.
例如,對於直線:
L1:y=x; L2:y=-x; L3:y=0
則L1和L2是可見的,L3是被覆蓋的.
給出n條直線,表示成y=Ax+B的形式(|A|,|B|<=500000),且n條直線兩兩不重合.求出所有可見的直線.


題解:根據題意我們可以想象出最後的圖形是一個下凹形的圖,如下圖:
紅色線段為
其中紅色線段為可見的線段,我們需要尋找的也是這些紅色線段屬於哪幾條線,我們可以得到斜率最小的和斜率最大的可以一直被看見,然後我們使用單調棧維護一個可以看的直線的棧。
首先我們對直線進行按照斜率排序(從小到大),如果斜率相同那麼就按照B從大到小(因為斜率相同的情況下,B大的能夠蓋住B小的),然後我們發現如果新加進來的一條直線如果能夠蓋住棧頂的直線的話那麼,他與棧頂元素的交點x座標要小於棧頂兩個直線的焦點,所以我們根據這個東西可以維護一下


a c   c o d e : ac\ code:

/**************************************************************
    Problem: 1007
    User: ReJ
    Language: C++
    Result: Accepted
    Time:264 ms
    Memory:3664 kb
****************************************************************/
 
#include <bits/stdc++.h>
using namespace
std; typedef long long ll; #define met(a, b) memset(a, b, sizeof(a)) #define rep(i, a, b) for(int i = a; i <= b; i++) #define per(i, a, b) for(int i = a; i >= b; i--) #define fi first #define se second const int maxn = 1e5 + 10; const int inf = 0x3f3f3f3f; const double eps = 1e-8; struct Line { double A, B; int id; void read(int _id) { id = _id; scanf("%lf%lf", &A, &B); } bool operator < (const Line & b) const { return A == b.A ? B > b.B : A < b.A; } } line[maxn]; inline double cal(Line a, Line b) { return (a.B - b.B) / (b.A - a.A); } int main() { int n; scanf("%d", &n); rep(i, 1, n) line[i].read(i); sort(line + 1, line + 1 + n); int sta[maxn], top =0; sta[++top] = 1; rep(i, 1, n) { if(fabs(line[i].A - line[i - 1].A) < eps)continue; while(top > 1 && cal(line[i], line[sta[top]]) <= cal(line[sta[top]], line[sta[top - 1]])) top--; sta[++top] = i; } bool vis[maxn]; met(vis, false); rep(i, 1, top) vis[line[sta[i]].id] = true; rep(i, 1, n) { if(vis[i]) printf("%d ", i); } puts(""); return 0; }