[HNOI2012] 射箭

题意:

在第一象限中给定n条平行于y轴,互不相交且不与坐标轴相交的线段。

求最大的x,使得存在一条过$(0,0)$的抛物线与前x条线段均相交。

$n\leq 10^{5},|x|,|y|\leq 10^{9}$。

题解:

显然先二分答案,然后考虑前x条线段是否合法。

设所求抛物线方程为$y=ax^{2}+bx+c$,根据题目中条件,有$a<0,b>0,c=0$。

考虑一条线段$(x,y0,y1)$对抛物线的约束,有$y0\leq ax^{2}+bx\leq y1$。

该式中$x,y0,y1$为已知量,$a,b$为变量,于是移项使得其符合函数形式,得到$b\geq -xa+\frac{y0}{x},b\leq -xa+\frac{y1}{x}$。

发现这个约束就相当于$(a,b)$需要位于两条直线中间夹的一段区域,那么把所有直线拿出来求一个半平面交即可。

一个答案合法(存在半平面交)当且仅当最后队列中有$\geq 3$条直线。

注意$(a,b)$需要位于第二象限,所以还需要添加四条线段将第二象限包围作为边界。

复杂度$O(n\log{n})$,有点卡常和卡精度,需要合理选取eps。

套路:

  • 推式子时注意“用已知量表示未知量”原则,当有两个未知量时尽量表示成函数形式。
  • 有范围限制的半平面交需要添加边界。

代码:

#include<bits/stdc++.h>
#define maxn 500005
#define maxm 500005
#define inf 1e12
#define eps 1e-15
#define ll long long
#define ld long double
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
struct node{
    ld x,y;
    node operator+(const node b)const{return (node){x+b.x,y+b.y};}
    node operator-(const node b)const{return (node){x-b.x,y-b.y};}
    ld operator*(const node b)const{return x*b.y-y*b.x;}
    node operator^(const ld b)const{return (node){x*b,y*b};}
};
struct Line{node a,b;ld k,d;int id;}A[maxn],L[maxn],Q[maxn];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline int dcmp(ld x){return (fabs(x)<=eps)?0:(x<0?-1:1); }
inline bool cmp(Line l1,Line l2){return (dcmp(l1.k-l2.k)==0)?(dcmp(l1.d-l2.d)<0):(dcmp(l1.k-l2.k)<0);}

inline bool onr(Line l,node c){return dcmp((c-l.a)*(l.b-l.a))>0;}
inline node ins(Line l1,Line l2){node u=l2.a-l1.a,v=l1.b-l1.a,w=l2.b-l2.a;ld k=(u*v)/(v*w);return l2.a+(w^k);}

inline bool check(int x,int n){
    rint tot=0,cnt=0,l=1,r=0;
    for(rint i=1;i<=n;i++) if(A[i].id<=x) L[++cnt]=A[i];
    for(rint i=1;i<=cnt;i++) if(dcmp(L[i].k-L[i+1].k)!=0) L[++tot]=L[i];
    for(rint i=1;i<=tot;Q[++r]=L[i++]){
        while(l<r && onr(L[i],ins(Q[r-1],Q[r]))) r--;
        while(l<r && onr(L[i],ins(Q[l],Q[l+1]))) l++;
    }
    while(l<r && onr(Q[l],ins(Q[r-1],Q[r]))) r--;
    while(l<r && onr(Q[r],ins(Q[l],Q[l+1]))) l++;
    return l<r-1;
}

int main(){
    int n=read(),cnt=0,l=1,r=n,ans=0;
    for(rint i=1;i<=n;i++){
        ld x=read(),y0=read(),y1=read();
        A[++cnt].a=(node){0,y0/x},A[cnt].b=(node){1,-x+y0/x},A[cnt].id=i;
        A[++cnt].a=(node){1,-x+y1/x},A[cnt].b=(node){0,y1/x},A[cnt].id=i;
    }
    A[++cnt].a=(node){-inf,eps},A[cnt].b=(node){-eps,eps},A[cnt].id=0;
    A[++cnt].a=(node){-eps,eps},A[cnt].b=(node){-eps,inf},A[cnt].id=0;
    A[++cnt].a=(node){-eps,inf},A[cnt].b=(node){-inf,inf},A[cnt].id=0;
    A[++cnt].a=(node){-inf,inf},A[cnt].b=(node){-inf,eps},A[cnt].id=0;
    for(rint i=1;i<=cnt;i++)
        A[i].k=atan2((A[i].b-A[i].a).y,(A[i].b-A[i].a).x),A[i].d=A[i].a.y-A[i].a.x*A[i].k;
    sort(A+1,A+1+cnt,cmp);
    while(l<=r){int mid=l+r>>1; if(check(mid,cnt)) ans=mid,l=mid+1; else r=mid-1;}
    printf("%d\n",ans);
    return 0;
}
射箭

猜你喜欢

转载自www.cnblogs.com/YSFAC/p/13191242.html