BZOJ2732[HNOI2012] 射箭

原题链接(不讲道理的卡精度):http://www.lydsy.com/JudgeOnline/problem.php?id=2732  
洛谷链接(不讲道理的卡时间):https://www.luogu.org/problemnew/show/P3222

射箭

Description

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

Input

输入文件第一行是一个正整数N,表示一共有N关。接下来有N行,第i+1行是用空格隔开的三个正整数xi,yi1,yi2(yi1

Output

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

Sample Input

5

2 8 12

5 4 5

3 8 10

6 2 3

1 3 7

Sample Output

3

HINT

题解

每个靶子应满足如下不等式:  

y1ax2+bxy2 y 1 ⩽ a x 2 + b x ⩽ y 2
 
其中,x为靶子的横坐标, y1,y2 y 1 , y 2 分别为下、上端点纵坐标。
以左半边不等式为例,进行如下变形:
y1ax2bx y 1 − a x 2 ⩽ b x

y1xaxb y 1 x − a x ⩽ b
 
其中, x,y1,y2 x , y 1 , y 2 都是已知量,所以,这事实上是个一次函数。  
b=ax+y1x b = − a x + y 1 x

显然,a是自变量,b是因变量,而上方的不等式组则变成下列形式:
{bax+y1xbax+y2x { b ⩾ − a x + y 1 x b ⩽ − a x + y 2 x

如此一来,这个问题就转化为半平面交问题, bax+y1x b ⩾ − a x + y 1 x 表示这条直线以上的半平面,同理 bax+y2x b ⩽ − a x + y 2 x 表示直线一下的半平面,我们只需要判定这些半平面交集是否为空即可。  
代码实现时,只需要调整一下结构体中起始点的顺序,就可以将“上下”两个方向转化为有向直线的左或右单一方向的问题。半平面交时,我们需要一个双端队列,每次检查队尾的两条直线的交点是否在新加入直线的右边,如果是,则弹出队尾元素,对于队首,我们需要进行同样的操作。所有元素添加完毕之后,还需要检查一遍首尾元素之间是否符合要求。最后,如果队列中剩余两个及以上的元素,那么半平面交存在。
为了求最多能过多少关,我们还需要一个二分枚举关卡。

在洛谷上,没有卡精度,eps取零即可,然而需要用long double,不过很容易T(卡常不讲道理),博主靠洛谷O2优化苟过。  
而BZOJ与之相反,不卡常,卡精度,取了许多eps都没过,弃疗。。。

代码
#include<bits/stdc++.h>
#define db long double
using namespace std;
const int M=2e5+5;
const int inf=1e9;
const db eps=0;
int n,tot,top;
struct pt{db x,y;};
struct li{pt x1,x2;db k;int id;};
int sig(db x){return (x>eps)-(x<-eps);}
pt operator *(pt a,db b){return (pt){a.x*b,a.y*b};}
pt operator /(pt a,db b){return (pt){a.x/b,a.y/b};}
pt operator +(pt a,pt b){return (pt){a.x+b.x,a.y+b.y};}
db operator *(pt a,pt b){return a.x*b.y-a.y*b.x;}
pt operator -(pt a,pt b){return (pt){a.x-b.x,a.y-b.y};}
db cal(db a,db b,db x){return b/a-a*x;}
bool operator <(li a,li b){return !sig(a.k-b.k)?sig((a.x2-a.x1)*(b.x1-a.x1))>0:a.k<b.k;}
li line[M],que[M],p[M];
int r,f;
char c;
int read()
{
    r=0;f=1;
    c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c))r=(r<<1)+(r<<3)+c-'0',c=getchar();
    return r*f;
}
void in()
{
    line[++tot].x1=(pt){-inf,-inf};line[tot].x2=(pt){inf,-inf};
    line[++tot].x1=(pt){inf,-inf};line[tot].x2=(pt){inf,inf};
    line[++tot].x1=(pt){inf,inf};line[tot].x2=(pt){-inf,inf};
    line[++tot].x1=(pt){-inf,inf};line[tot].x2=(pt){-inf,-inf};
    n=read();
    db x,y1,y2;
    for(int i=1;i<=n;++i)
    {
        x=read();y1=read();y2=read();
        line[++tot].x1=(pt){-1000,cal(x,y1,-1000)};
        line[tot].x2=(pt){1000,cal(x,y1,1000)};
        line[++tot].x1=(pt){1000,cal(x,y2,1000)};
        line[tot].x2=(pt){-1000,cal(x,y2,-1000)};
        line[tot].id=line[tot-1].id=i;
    }
}
pt inter(li a,li b)
{
    db k1,k2;
    k1=(b.x2-a.x1)*(a.x2-a.x1);
    k2=(a.x2-a.x1)*(b.x1-a.x1);
    return (b.x2-b.x1)*k2/(k1+k2)+b.x1;
}
bool check(li a,li b,li t)
{
    pt p=inter(a,b);
    return sig((p-t.x1)*(t.x2-t.x1))>0;
}
bool hpi(int x)
{
    top=0;
    for(int i=1;i<=tot;++i)
    {
        if(line[i].id<=x)
        {
            if(line[i].k!=p[top].k)++top;
            p[top]=line[i];
        }
    }
    int le=1,ri=0;
    que[++ri]=p[1];que[++ri]=p[2];
    for(int i=3;i<=top;++i)
    {
        while(le<ri&&check(que[ri-1],que[ri],p[i]))--ri;
        while(le<ri&&check(que[le+1],que[le],p[i]))++le;
        que[++ri]=p[i];
    }
    while(le<ri&&check(que[ri-1],que[ri],que[le]))--ri;
    while(le<ri&&check(que[le+1],que[le],que[ri]))++le;
    return (ri-le)>=2;
}
void ac()
{
    for(int i=1;i<=tot;++i)
    line[i].k=atan2(line[i].x2.y-line[i].x1.y,line[i].x2.x-line[i].x1.x);
    sort(line+1,line+1+tot);
    int le=1,ri=n,ans=0,mid;
    while(le<=ri)
    {
        mid=(le+ri)>>1;
        if(hpi(mid))ans=mid,le=mid+1;
        else ri=mid-1;
    }
    printf("%d",ans);
}
int main()
{
    in();ac();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值