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

=== ===

这里放传送门

=== ===

题解

这题很容易想到按照解析式 y=ax2+bx 和给定的纵坐标范围列出方程然后求半平面交看看有没有交集,也就是会列出一些 yi,1ax2i+bxiyi,2 这样的东西来,其中 xi yi 都是已知量。因为这个题数据范围比较大所以需要用 O(nlogn) 的半平面交,那么就不能支持在线算法,需要提前知道所有要用的半平面,所以又加入了二分,二分当前能打到哪一关就可以了。

然后就是炸精度,炸精度,炸精度。。。。一开始在直线选点的时候选了和坐标轴的焦点,但这样的话万一遇到过原点的直线就不好办了。然后就是eps不能选的太大,因为当坐标很大的时候做除法会把数字除到1e-9那个级别去。最后加了long double才过掉。。

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const long double eps=1e-16;
const long double inf=1e11;
const long double P=-1.0;
const long double Q=4.0;
int n,N,head,tail;
long double X[200010],Y[200010],Yy[200010];
struct Vector{
    long double x,y;
    Vector(long double X=0,long double Y=0){x=X;y=Y;}
    Vector operator + (const Vector &a){return Vector(x+a.x,y+a.y);}
    Vector operator - (const Vector &a){return Vector(x-a.x,y-a.y);}
    long double operator * (const Vector &a){return x*a.y-y*a.x;}
    Vector mul(long double t){return Vector(x*t,y*t);}
}poly[200010],p[200010];
struct Line{
    Vector P,v;
    long double ang;
    Line(){P=Vector();v=Vector();}
    Line(Vector A,Vector B){
        P=A;v=B-A;ang=atan2(v.y,v.x);
    }
}L[200010],q[200010];
int comp(Line a,Line b){return a.ang<b.ang;}
Vector GLI(Line A,Line B){
    Vector u=A.P-B.P;
    long double t=(B.v*u)/(A.v*B.v);
    return A.P+A.v.mul(t);
}
bool Onleft(Line A,Vector w){return A.v*(w-A.P)>-eps;}
bool HalfpIns(){
    sort(L+1,L+N+1,comp);
    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 (fabs(q[tail].v*q[tail-1].v)<eps){
            --tail;
            if (Onleft(q[tail],L[i].P))
              q[tail]=L[i];
        }
        if (head<tail) p[tail-1]=GLI(q[tail],q[tail-1]);
    }
    while (head<tail&&!Onleft(q[head],p[tail-1])) --tail;
    if (tail-head<=1) return false;
    return true;
}
bool check(int mid){
    Vector A,B,v;N=0;
    long double x,y,yy,M1,M2;
    for (int i=1;i<=mid;i++){
        x=X[i];y=Y[i];yy=Yy[i];
        y-=eps;yy+=eps;
        B=Vector(P,(yy-x*x*P)/x);
        A=Vector(Q,(yy-x*x*Q)/x);
        L[++N]=Line(A,B);
        A=Vector(P,(y-x*x*P)/x);
        B=Vector(Q,(y-x*x*Q)/x);
        L[++N]=Line(A,B);
    }
    L[++N]=Line(Vector(eps,inf),Vector(-inf,inf));
    L[++N]=Line(Vector(-inf,inf),Vector(-inf,eps));
    L[++N]=Line(Vector(-inf,eps),Vector(eps,eps));
    L[++N]=Line(Vector(eps,eps),Vector(eps,inf));
    return HalfpIns();
}
inline int getnum() {
    int ans = 0; char c; bool flag = false;
    while ((c = getchar()) == ' ' || c == '\n' || c == '\r');
    if (c == '-') flag = true; else ans = c - '0';
    while ((c = getchar()) >= '0' && c <= '9') ans = ans * 10 + c - '0';
    return ans * (flag ? -1 : 1);
}
int Divide(int l,int r){
    int mid,ans=0;
    while (l<=r){
        mid=(l+r)>>1;
        if (check(mid)){ans=max(ans,mid);l=mid+1;}
        else r=mid-1;
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        X[i]=getnum();Y[i]=getnum();Yy[i]=getnum();
    }
    printf("%d\n",Divide(1,n));
    return 0;
}

偏偏在最后出现的补充说明

背板子,背板子,背板子T_T

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值