BZOJ 2732 二分+半平面相交 解题报告

2732: [HNOI2012]射箭

Description

沫沫最近在玩一个二维的射箭游戏,如下图 1 所示,这个游戏中的 x 轴在地面,第一象限中有一些竖直线段作为靶子,任意两个靶子都没有公共部分,也不会接触坐标轴。沫沫控制一个位于(0,0)的弓箭手,可以朝 0 至 90?中的任意角度(不包括 0度和 90度),以任意大小的力量射出带有穿透能力的光之箭。由于游戏中没有空气阻力,并且光之箭没有箭身,箭的轨迹会是一条标准的抛物线,被轨迹穿过的所有靶子都认为被沫沫射中了,包括那些 只有端点被射中的靶子。这个游戏有多种模式,其中沫沫最喜欢的是闯关模式。在闯关模式中,第一关只有一个靶 子,射中这个靶子即可进入第二关,这时在第一关的基础上会出现另外一个靶子,若能够一箭 双雕射中这两个靶子便可进入第三关,这时会出现第三个靶子。依此类推,每过一关都会新出 现一个靶子,在第 K 关必须一箭射中前 K 关出现的所有 K 个靶子才能进入第 K+1 关,否则游戏 结束。沫沫花了很多时间在这个游戏上,却最多只能玩到第七关“七星连珠”,这让她非常困惑。 于是她设法获得了每一关出现的靶子的位置,想让你告诉她,最多能通过多少关

Input

输入文件第一行是一个正整数N,表示一共有N关。接下来有N行,第i+1行是用空格隔开的三个正整数xi,yi1,yi2(yi1 < yi2 ),表示第i关出现的靶子的横坐标是xi,纵坐标的范围是从yi1到yi2 。
输入保证30%的数据满足N≤100,50%的数据满足N≤5000,100%的数据满足N≤100000且给 出的所有坐标不超过109 。

Output

仅包含一个整数,表示最多的通关数。

Sample Input

5
2 8 12
5 4 5
3 8 10
6 2 3
1 3 7

Sample Output

3
【解题报告】
设这个经过原点的抛物线为y = a * x ^ 2 + b * x,设一条线段的起点和终点为(x0,y1)和(x0,y2),且y2 > y1。
将x0带入到设出的抛物线中,会得到y = a * x0 ^ 2 + b * x0,这时候需要满足的是y <= y2 && y >= y1,也就是a * x0 ^ 2 + b * x0 <= y2 && y1 <= a * x0 ^ 2 + b * x0
整理一下思路,x0,y1,y2是已知量,a和b是我们设出来的量,不妨换一种写法,令x = a,y = b,k = x0,那么原不等式组就是
=> x * k ^ 2 + y * k <= y2 && x * k ^ 2 + y * k >= y1
=> x * k ^ 2 + y * k - y2 <= 0 && x * k ^2 + y * k - y1 >= 0
这样就很明显了,不等式组化成了两个半平面,之后利用半平面交判定是否存在就可以了。最外层套一个二分,时间复杂度大概是O(nlog^2n)

然而

这道题玄学卡精度!!!
我花了40分钟各种调,最后把eps改成1e-50才过。。。
给出题人寄刀片!!!

这里写图片描述

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 200010

struct Point
{  
    long double x,y;         
    Point(long double _ = .0,long double __ = .0):x(_),y(__) {}  
    Point operator +(const Point &a)const 
    {  
        return Point(x+a.x,y+a.y);  
    }  
    Point operator -(const Point &a)const 
    {  
        return Point(x-a.x,y-a.y);  
    }  
    Point operator *(double a)const 
    {  
        return Point(x*a,y*a);  
    }  
}p[N];
struct Line
{
    Point p,v;
    long double alpha;
    Line(Point _,Point __):p(_),v(__) 
    {  
        alpha=atan2(v.y,v.x);  
    }Line(){}  
    bool operator <(const Line &a)const 
    {  
        return alpha<a.alpha;  
    }  
}src[N],line[N],q[N];
int t,lines;

inline long double Cross(Point p1,Point p2)  
{  
    return p1.x*p2.y-p1.y*p2.x;  
}  
inline bool OnLeft(Point p,Line l)  
{  
    return Cross(l.v,p-l.p)>=0;//left or right
}  
inline Point GetIntersection(Line l1,Line l2)  
{  
    Point u=l1.p-l2.p;  
    long double temp=Cross(l2.v,u)/Cross(l1.v,l2.v);  
    return l1.p+l1.v*temp;  
}  

#define EPS 1e-50
#define DCMP(a) (fabs(a)<EPS)  

inline bool HalfplaneIntersection(int lines)  
{  
    int front=1,tail=1;  
    q[1]=line[1];  
    for(int i=2;i<=lines;++i) 
    {  
        while(front<tail&&!OnLeft(p[tail-1],line[i])) --tail;  
        while(front<tail&&!OnLeft(p[front],line[i])) ++front;  
        if(DCMP(Cross(q[tail].v,line[i].v)))  
            q[tail]=OnLeft(q[tail].p,line[i])?q[tail]:line[i];  
        else q[++tail]=line[i];  
        if(front<tail) p[tail-1]=GetIntersection(q[tail],q[tail-1]);  
    }  
    while(front<tail&&!OnLeft(p[tail-1],q[front])) --tail;  
    return tail-front>1;  
}  
inline bool Judge(int mid)
{
    mid<<=1;  
    memcpy(line+1,src+1,sizeof(Line)*mid);  
    sort(line+1,line+mid+1);  
    return HalfplaneIntersection(mid);  
}
int main()
{
    scanf("%d",&t);
    for(int i=1;i<=t;++i)
    {
        static double x,y1,y2;
        scanf("%lf%lf%lf",&x,&y1,&y2);
        src[++lines]=Line(Point(0,y2/x),Point(-1/x,1));  
        src[++lines]=Line(Point(0,y1/x),Point(1/x,-1));  
    }
    int l=1,r=t,ans=1;
    while(l<=r)
    {
        int mid=(l + r)>>1;  
        if(Judge(mid))  
            ans=mid,l=mid+1;  
        else r=mid-1;  
    }
    printf("%d\n",ans);
    return 0;
}

让我看到你们的双手

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值