[BZOJ1007][HNOI2008]水平可见直线(单调栈+计算几何)

来源:互联网 发布:御名阁宝宝起名软件 编辑:程序博客网 时间:2024/06/08 05:20

题目描述

传送门

题解

由题意可知能看见的直线一定呈下凸。
以k为第一关键字,b为第二关键字排序,得到斜率不降、斜率相等时截距不降的直线序列。维护自底向上斜率单增的单调栈,当直线q[r-1]和当前枚举到的直线i共同将q[r]的最大值覆盖或者枚举到的i和q[r]平行时将q[r]弹出,最后在栈内的直线即为可以看到的直线。

代码

#include<algorithm> #include<iostream>#include<cstring>#include<cstdio>using namespace std;#define LL long longconst int max_n=5e4+5;int n,l,r,cnt,q[max_n],ans[max_n];struct hp{int k,b,id;}a[max_n];inline int cmp(hp a,hp b){    return a.k<b.k||(a.k==b.k&&a.b<b.b);}inline bool check(int x1,int x2,int x3){    LL w1=(LL)(a[x1].k-a[x3].k)*(LL)(a[x2].b-a[x1].b);    LL w2=(LL)(a[x1].k-a[x2].k)*(LL)(a[x3].b-a[x1].b);    return w1>=w2;}int main(){    scanf("%d",&n);    for (int i=1;i<=n;++i)        scanf("%d%d",&a[i].k,&a[i].b),a[i].id=i;    sort(a+1,a+n+1,cmp);    l=r=0;    q[++r]=1;    for (int i=2;i<=n;++i)    {        while (l<r-1&&check(i,q[r-1],q[r])) r--;        while (l<r&&a[q[r]].k==a[i].k) r--;        q[++r]=i;    }    for (int i=l+1;i<=r;++i)        ans[++cnt]=a[q[i]].id;    sort(ans+1,ans+cnt+1);    for (int i=1;i<=cnt;++i)        printf("%d ",ans[i]);}
0 0