[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。
套路:
- 推式子时:注意“用已知量表示未知量”原则,当有两个未知量时尽量表示成函数形式。
- 有范围限制的半平面交$\rightarrow$需要添加边界。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }