bzoj 1007 [HNOI2008]水平可见直线

http://www.elijahqi.win/archives/3484
Description
  在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条直线两两不重合.求出所有可见的直线.

Input
  第一行为N(0 < N < 50000),接下来的N行输入Ai,Bi

Output
  从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格

Sample Input
3
-1 0
1 0
0 0
Sample Output
1 2
HINT

Source
证明:http://trinkle.blog.uoj.ac/blog/235

补充:上午看了很久这个 因为数学基础太差 这里有一个对偶的问题

我们可以更加形象的不通过画图想象的问题

首先我能看见的一定都是在一个下凸壳上的东西 那么我将其转化为点值之后即y=kx+b

点=(k,-b) 转化为这之后我们求上凸壳即可 这是为什么 我们考虑下凸壳中最后一条直线我们一定是斜率最大的点 然后我们考虑两条直线k1,-b1 k,-b

我们重新在点的这个坐标中把他们的斜率写出来 发现正好可以用k1,k,b,b1相互表示并且凹凸性一致 其他详细证明参照上面神犇blog,蒟蒻elijahqi对此只是浅显的了解

#include<cstdio>
#include<cctype>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if(T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=5e4+10;
int n,q[N],top;
struct node{int x,y,id;}a[N];
inline node operator-(const node &a,const node &b){return (node){a.x-b.x,a.y-b.y};}
inline ll operator*(const node &a,const node &b){return (ll)a.x*b.y-(ll)a.y*b.x;}
inline ll cross(const node &a,const node &b,const node &c){return(b-a)*(c-a);}
inline bool cmp(const node &a,const node &b){return a.x==b.x?a.y<b.y:a.x<b.x;}
int main(){
//  freopen("bzoj1007.in","r",stdin);
    n=read();
    for (int i=1;i<=n;++i) a[i]=(node){read(),-read(),i};
    sort(a+1,a+n+1,cmp);
    for (int i=1;i<=n;++i){
        if (a[i].x!=a[i-1].x||i==1) {
            while(top>1&&cross(a[q[top-1]],a[q[top]],a[i])<=0)--top;q[++top]=i;
        }
    }
    for (int i=1;i<=top;++i) q[i]=a[q[i]].id;sort(q+1,q+top+1);
    for (int i=1;i<=top;++i) printf("%d ",q[i]);puts("");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值