• [HNOI2012] 射箭


    题意:

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

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

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

    题解:

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

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

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

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

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

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

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

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

    套路:

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

    代码:

    #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
    ",ans);
        return 0;
    }
    射箭
  • 相关阅读:
    1108递归
    1108JS函数
    1107数组例子
    1106JS循环
    1106JS数组
    1104JS语法
    1104JS循环
    PDO概念 分析 练习
    Jquery网页元素里面的操作以及JSON
    Jquery事件
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13191242.html
Copyright © 2020-2023  润新知