[BZOJ2732][HNOI2012]射箭(二分+半平面交)

本文介绍了一种结合二分查找与半平面交算法解决特定几何问题的方法。通过分析给定条件下的数学模型,利用直线特性进行求解。特别讨论了如何避免选择特殊点以确保算法的正确性,并分享了实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

我是超链接

题解:

这个关卡包含前面的特性很明显是二分
判断的话随便画画柿子就有 y1<=ax2+bx<=y2 ,而 x,y1,y2 都是已知的,这不就是一个关于a,b的直线了吗,半平面交?
这个数据范围也只有nlogn的半平面交能过了吧,总复杂度 O(nlog2n)
选择直线上的点的时候,注意不能选择与坐标轴的交点,因为这样过原点的直线并没有办法表示,那就随便选一个呗= =
因为这里的直线不一定能交出啥来,我们必须在外面加一个巨大的框,注意这里加的是a,b的框,a是一定小于0的,要求b大于0,对称轴在右。
还有就是判断在线右边的时候一定要加上‘=’,选eps和INF都要好好考虑,INF不能选的太大,eps不能太大也不能太小= =,double也换掉。
在结构体里重载运算符是非常快的。

代码:

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const long double INF=1e11;//
const int N=200050;
const long double eps=1e-12;//
const long double X1=3.0;
const long double X2=-1.0;
int dcmp(long double x)
{
    if (x>=-eps && x<=eps) return 0;
    return (x>0)?1:-1;
};
struct po
{
    long double x,y;
    po (long double X=0,long double Y=0){x=X;y=Y;}
}p[N],d[N];
struct line
{
    po x,v;long double ang;
    line (po X=po(0,0),po V=po(0,0)){x=X; v=V; ang=atan2(v.y,v.x);}
    bool operator <(line a)const {return ang<a.ang;}
}L[N],q[N],s[N];int head,tail;
po operator -(po x,po y){return po(x.x-y.x,x.y-y.y);}
po operator +(po x,po y){return po(x.x+y.x,x.y+y.y);}
po operator *(po x,long double y){return po(x.x*y,x.y*y);}
long double cj(po x,po y){return x.x*y.y-x.y*y.x;}
po jd(line a,line b)
{
    po u=a.x-b.x;
    long double t=cj(b.v,u)/cj(a.v,b.v);
    return a.x+a.v*t;
}
bool Onleft(line x,po y){return dcmp(cj(x.v,y-x.x))>=0;} //
bool halfp(int n)
{
    for (int i=1;i<=n;i++) L[i]=s[i];
    sort(L+1,L+n+1);
    head=tail=1; q[1]=L[1];
    for (int i=2;i<=n;i++)
    {
        while (head<tail && !Onleft(L[i],p[tail-1])) tail--;
        while (head<tail && !Onleft(L[i],p[head])) head++;
        q[++tail]=L[i];
        if (dcmp(cj(q[tail].v,q[tail-1].v))==0)
        {
            tail--;
            if (Onleft(q[tail],L[i].x)) q[tail]=L[i];
        }
        if (head<tail) p[tail-1]=jd(q[tail],q[tail-1]);
    }
    while (head<tail && !Onleft(q[head],p[tail-1])) tail--;
    if (tail-head<=1) return 0;return 1;//
}
int read()
{
    char c=getchar();int x=0,f=1;
    while (c<'0' || c>'9'){if (c=='-') f=-1;c=getchar();}
    while (c>='0' && c<='9') x=x*10+c-'0',c=getchar();
    return f*x;
}
int main()
{
    long double x,y1,y2;int n,id=0;
    n=read();
    s[++id]=line(po(0,0),po(0,INF));
    s[++id]=line(po(0,INF),po(-INF,0));
    s[++id]=line(po(-INF,INF),po(0,-INF));
    s[++id]=line(po(-INF,0),po(INF,0));
    for (int i=1;i<=n;i++)
    {
        x=read(); y1=read(); y2=read();
        po a=po(X1,(y1-x*x*X1)/x);
        po b=po(X2,(y1-x*x*X2)/x);
        s[++id]=line(b,a-b);
        a=po(X1,(y2-x*x*X1)/x);
        b=po(X2,(y2-x*x*X2)/x);
        s[++id]=line(a,b-a);
    }
    int l=1,r=n,ans;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (halfp(mid*2+4)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值