BZOJ1007 [HNOI2008]水平可见直线 计算几何 单调栈维护上凸包

大家都很强,可与之共勉。

题意:
  给您 n 个直线,让您求出哪些直线从最上方打一束光能够被照到。
  其中0<n5e4,直线以 y=ax+b 的形式给出。

题解:
  我们发现其实题目很像一个求开口向上的凸包。
  于是我们考虑这个开口向上的凸包的性质,发现它是这样的

  我们发现这样的图(橙色部分)从左往右它一定是斜率增加,横坐标依次增加(是句废话)。

  所以我们这样去维护就可以了。
  先考虑使斜率斜率递增,排序就可以了。
  对于一条线的它被遮住了,当且仅当斜率比它大的线和斜率比它小的直线的交点横坐标小于它与斜率比它小的直线的交点横坐标(如图中的蓝点和红点,原谅色的线就 GG 了)。我们用单调栈( x <script type="math/tex" id="MathJax-Element-48">x</script>轴坐标单调且斜率单调)就可以了。

  注意对于斜率相同的直线只保留最上面的。

  代码如下:

  STL大法好

/**************************************************************
    Problem: 1007
    User: Lazer2001
    Language: C++
    Result: Accepted
    Time:424 ms
    Memory:4708 kb
****************************************************************/

# include <bits/stdc++.h>

# define N 50010

std :: pair < double, double > l [N], lines [N] ;
std :: vector < std :: pair < double, double > > st ;
std :: map < std :: pair < double, double >, int > S ;

# undef N

inline double Get_x ( std :: pair < double, double >& a, std :: pair < double, double >& b )  {
    return ( a.second - b.second ) / ( b.first - a.first ) ;
}

int main ( )  {
    int n ;
    scanf ( "%d", & n ) ;
    for ( int i = 1 ; i <= n ; ++ i )  {
        static double a, b ;
        scanf ( "%lf%lf", & a, & b ) ;
        l [i] = std :: make_pair ( a, b ) ;
        S [l [i]] = i ;
    }
    std :: sort ( l + 1, l + 1 + n ) ;
    int cnt ( 0 ) ;

    for ( int i = 1 ; i <= n ; ++ i )  {
        while ( l [i + 1].first == l [i].first && l [i + 1].second > l [i].second )  ++ i ;
        lines [++ cnt] = l [i] ;
    }

    for ( int i = 1 ; i <= cnt ; ++ i )  {
        while ( st.size ( ) > 1 && Get_x ( lines [i], st [st.size ( ) - 1] ) <= Get_x ( st [st.size ( ) - 2], st [st.size ( ) - 1] ) )  st.pop_back ( ) ;
        st.push_back ( lines [i] ) ;
    }

    std :: set < int > ans ;
    for ( std :: vector < std :: pair < double, double > > :: iterator it = st.begin ( ) ; it != st.end ( ) ; ++ it )  {
        ans.insert ( S.find ( *it ) -> second ) ;
    }

    for ( std :: set < int > :: iterator it = ans.begin ( ) ; it != ans.end ( ) ; ++ it )  {
        printf ( "%d ", *it ) ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值