1007: [HNOI2008]水平可见直线[单调栈]

6 篇文章 0 订阅
1 篇文章 0 订阅

题意:  
在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: 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值