bzoj 2732: [HNOI2012]射箭

题意

给你若干个靶子
你可以完美地打出一个二次函数
问你最多可以穿过前面多少个靶子

题解

很容易想到二分答案
然后设这个二次函数是 y=ax2+bx y = a x 2 + b x
联系前面x个靶子可以得到若干个不等式
用半平面交判是否无解就可以了
一开始WA了。。改成long doubleT了。。
改回double,然后不知道又搞了什么就A了。。

CODE:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef double LB;
const int N=100005*2;
const LB eps=1e-18;
const LB MAX=1e9+5;
int n;
struct pnt
{   
    LB x,y;
    pnt(){}
    pnt(LB x1,LB y1) {x=x1;y=y1;}
};
struct qq
{
    pnt x,y;
    LB angle;
/*  qq (){}
    void print ()
    {
        printf("x:%lf %lf y:%lf %lf\n",x.x,x.y,y.x,y.y);
    }*/
}s[N];int tot=0;
struct qr{LB l,r1,r2;}A[N];
void add (LB x1,LB y1,LB x2,LB y2)
{
    tot++;
    s[tot].x=pnt(x1,y1);
    s[tot].y=pnt(x2,y2);
    s[tot].angle=atan2(y2-y1,x2-x1);
}
void Ins (LB a,LB b,LB c)//ax+by<=c
{
    if (abs(b)<-eps)
    {
        if (a>eps) add(c/a,0,c/a,1);
        else add(c/a,1,c/a,0);
        return ;
    }
    pnt x1=pnt(0,c/b),x2=pnt(1,(c-a)/b);
    if (b>eps)  add(1,(c-a)/b,0,c/b);
    else add(0,c/b,1,(c-a)/b);
}
LB mul (pnt x,pnt y,pnt z)
{
    LB x1=x.x-z.x,x2=y.x-z.x;
    LB y1=x.y-z.y,y2=y.y-z.y;
    return x1*y2-x2*y1;
}
bool cmp (qq x,qq y)
{
    if (abs(x.angle-y.angle)<eps)   return mul(x.y,y.y,x.x)<0;
    return x.angle<y.angle;
}
pnt JD (qq x,qq y)
{
    LB s1=mul(x.y,y.x,x.x);
    LB s2=mul(y.y,x.y,x.x);
    pnt xx;
    xx.x=(y.x.x*s2+y.y.x*s1)/(s1+s2);
    xx.y=(y.x.y*s2+y.y.y*s1)/(s1+s2);
    return xx;
}
bool check (qq x,qq a,qq b)
{
    pnt xx=JD(a,b);
    return mul(x.y,xx,x.x)<-eps;
}
int q[N];
pnt ans[N];
int nn;
bool solve ()
{
    sort(s+1,s+1+tot,cmp);
    nn=1;
    for (int u=2;u<=tot;u++)
        if (s[u].angle-s[u-1].angle>eps)
            s[++nn]=s[u];
    tot=nn;
    int st=1,ed=2;
    q[1]=1;q[2]=2;
    for (int u=3;u<=tot;u++)
    {
        while (st<ed&&check(s[u],s[q[ed]],s[q[ed-1]])) ed--;
        while (st<ed&&check(s[u],s[q[st]],s[q[st+1]])) st++;
        q[++ed]=u;
    }
    while (st<ed&&check(s[q[st]],s[q[ed]],s[q[ed-1]])) ed--;
    while (st<ed&&check(s[q[ed]],s[q[st]],s[q[st+1]])) st++;
    s[++ed]=s[st];
    return ed-st>2;
}
bool check (int x)//前x个是不是都可以打中 
{
    tot=0;
    add(0,0,0,1);
    add(0,0,1,0);
    add(-MAX,1,-MAX,0);
    add(1,MAX,0,MAX);   //printf("%d\n",tot);
    for (int u=1;u<=x;u++)
    {
        Ins(-(A[u].l*A[u].l),-A[u].l,-A[u].r1);
        Ins(A[u].l*A[u].l,A[u].l,A[u].r2);
    }
    /*for (int u=1;u<=tot;u++)
        s[u].print();*/
    return solve();
//  printf("YES:%d\n",nn);
    //return nn>2;
}
int main()
{
//  freopen("input10.in","r",stdin);
    scanf("%d",&n);
    for (int u=1;u<=n;u++)
        scanf("%lf%lf%lf",&A[u].l,&A[u].r1,&A[u].r2);
//  printf("%d\n",n);
    //check(1);
    int l=1,r=n;
    int ans=0;
    while (l<=r)
    {
        int mid=(l+r)>>1;
    //  printf("%d %d %d\n",l,r,mid);
        if (check(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、付费专栏及课程。

余额充值