【幾何】[HNOI2008][BZOJ1007]水平可見直線

題目描述

在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條直線兩兩不重合.求出所有可見的直線.

第一行爲N(0 < N < 50000),接下來的N行輸入Ai,Bi

從小到大輸出可見直線的編號,兩兩中間用空格隔開,最後一個數字後面也必須有個空格

樣例輸入

3
-1 0
1 0
0 0

樣例輸出

1 2

題目分析

首先我們看到題目可以發現,我們其實維護的是一個向下的凸包,那麼我們按照斜率優化的方法搞一搞,首先我們將每一條直線按照K進行排序,然後我們發現如果上一個交點在當前直線的下方,那麼當前直線一定覆蓋了上一條直線,這個畫個圖就明白了。然後最後棧裏剩下的直線就是答案(這道題目主要是難寫)

代碼

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <stack>
#include <cmath>
#define mcp(a,b) fabs((a)-(b))<eps
using namespace std;
const int MAXN = 500000;
const double eps = 1e-8;
const int INF = 1000000000;
struct Point{
    double x, y;
    Point(){x=y=0;}
};
struct Line{
    double a, b;
    int id;
    Point GetD(const Line& c){
        Point ret;
        ret.x = (1.0 * (c.b - b)) / (a - c.a);
        ret.y = a * ret.x + b;
        return ret;
    }
    bool operator == (const Line& c) {
        return c.a + eps >= a && c.a - eps <= a;
    }
}T[MAXN+10];
stack<Line> ans;
stack<Point> jd;
bool cmp(Line a, Line b){
    if(mcp(a.a, b.a))
        return a.b < b.b;
    return a.a < b.a;
}
int pcmp(Point p, Line l){
    double y = l.a * p.x + l.b;
    if(y >= p.y-eps)
        return -1;
    return 1;
}
bool check[MAXN+10];
int main(){
    Point tmp;
    int n, tn=0;
    scanf("%d", &n);
    for(int i=1;i<=n;i++){
        scanf("%lf%lf", &T[i].a, &T[i].b);
        T[i].id = i;
    }
    sort(T+1, T+1+n, cmp);
    for(int i=1;i<=n;i++){
        while(mcp(T[i].a,T[i+1].a)) i++;
        T[++tn] = T[i];
    }
    ans.push(T[1]);
    for(int i=2;i<=tn;i++){
        while(!jd.empty()){
            Point tp = jd.top();
            if(pcmp(tp, T[i]) <= 0){
                jd.pop();
                ans.pop();
            }else break;
        }
        jd.push(T[i].GetD(ans.top()));
        ans.push(T[i]);
    }
    while(!ans.empty()){
        check[ans.top().id] = true;
        ans.pop();
    }
    bool fir = false;
    for(int i=1;i<=n;i++)
        if(check[i]){
            if(fir)
                printf(" %d", i);
            else{
                printf("%d", i);
                fir = true;
            }
        }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章