取到最大值的位置有 2 种情况:
- 在二次函数顶点:可用树套树维护。
- 在
l 或 r 上:建棵线段树,每个节点存区间内所有二次函数的轮廓线,查询时在轮廓线上二分。合并2 个轮廓线时先求出所有区间,再对每个区间不停求交点。#include<bits/stdc++.h> using namespace std; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline void Read(int& x){ char c=nc(); for(;c<'0'||c>'9';c=nc()); for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc()); } typedef double db; const int N=50010; const int M=8000000; const db Eps=1e-10; #define pb push_back struct node{ int x,y,p; db a,b,c; inline db Get(db x){ return a*x*x+b*x+c; } }a[N]; struct Node{ db l,r;int id; Node(db l=0,db r=0,int id=0):l(l),r(r),id(id){} }; int k,n,m,p,x,y; int mx,num,X,Y; int Rt[N<<2]; int c[M],ls[M],rs[M]; db q[N<<2],Q[N<<2],Ans; vector<Node>g[N<<2],t; void Update(int& x,int l,int r,int y,int z){ if(!x)x=++num; c[x]=z; if(l==r)return; int Mid=l+r>>1; if(y<=Mid)Update(ls[x],l,Mid,y,z);else Update(rs[x],Mid+1,r,y,z); } int Merge(int x,int y,int l,int r){ if(!x||!y)return x+y; if(l==r)return c[x]>c[y]?x:y; int z=++num; int Mid=l+r>>1; ls[z]=Merge(ls[x],ls[y],l,Mid); rs[z]=Merge(rs[x],rs[y],Mid+1,r); c[z]=max(c[ls[z]],c[rs[z]]); return z; } void build(int x,int l,int r){ if(l==r){ Update(Rt[x],0,mx,a[l].x,a[l].y); return; } int Mid=l+r>>1; build(x<<1,l,Mid);build(x<<1|1,Mid+1,r); Rt[x]=Merge(Rt[x<<1],Rt[x<<1|1],0,mx); } int query2(int x,int l,int r,int L,int R){ if(!x||l>R||r<L)return 0; if(l>=L&&r<=R)return c[x]; int Mid=l+r>>1; return max(query2(ls[x],l,Mid,L,R),query2(rs[x],Mid+1,r,L,R)); } int query(int x,int l,int r,int L,int R,int y,int z){ if(l>R||r<L)return 0; if(l>=L&&r<=R)return query2(Rt[x],0,mx,y,z); int Mid=l+r>>1; return max(query(x<<1,l,Mid,L,R,y,z),query(x<<1|1,Mid+1,r,L,R,y,z)); } inline db Calc(int i,int j,db l,db r){ node x=a[i],y=a[j]; db a=x.a-y.a,b=x.b-y.b,c=x.c-y.c; if(fabs(a)<Eps){ if(fabs(b)<Eps)return r; db Ans=-c/b; if(Ans>l+Eps&&Ans+Eps<r)return Ans; return r; } db d=b*b-a*c*4; if(d<-Eps)return r; d=sqrt(d); db w=(-b-d)/a/2; db Ans=r; if(w>l+Eps&&w+Eps<r)Ans=w; w=(-b+d)/a/2; if(w>l+Eps&&w+Eps<r)Ans=min(Ans,w); return Ans; } inline void Merge(vector<Node>&A,vector<Node>&B,vector<Node>&C,int x){ int la=A.size(),lb=B.size(); if(!la){ C=B;return; } if(!lb){ C=A; return; } int p1=0,p2=0,L=0; for(int i=0;i<la;i++)Q[++L]=A[i].l,Q[++L]=A[i].r; for(int i=0;i<lb;i++)Q[++L]=B[i].l,Q[++L]=B[i].r; sort(Q+1,Q+L+1); int l=0; for(int i=1;i<=L;){ int j=i; for(;j<L&&fabs(Q[j+1]-Q[j])<Eps;j++); q[++l]=Q[i];i=j+1; } p1=0;p2=0;t.clear(); for(int i=1;i<l;i++){ db L=q[i],R=q[i+1]; while(p1<la&&A[p1].r+Eps<R)p1++; while(p2<lb&&B[p2].r+Eps<R)p2++; if((p1==la||A[p1].l>L+Eps)&&(p2==lb||B[p2].l>L+Eps))continue; if(p1==la||A[p1].l>L+Eps){ t.pb(Node(L,R,B[p2].id)); continue; } if(p2==lb||B[p2].l>L+Eps){ t.pb(Node(L,R,A[p1].id)); continue; } while(L+Eps<R){ db w=Calc(A[p1].id,B[p2].id,L,R); db Mid=(w+L)/2; if(a[A[p1].id].Get(Mid)>a[B[p2].id].Get(Mid))t.pb(Node(L,w,A[p1].id)); else t.pb(Node(L,w,B[p2].id)); L=w; } } C.clear();l=t.size(); for(int i=0;i<l;){ int j; for(j=i;j<l-1&&t[j+1].id==t[i].id;j++); C.pb(Node(t[i].l,t[j].r,t[i].id)); i=j+1; } } void Build(int x,int l,int r){ if(l==r){ g[x].pb(Node(a[l].p,a[l].x*2-a[l].p,l)); return; } int Mid=l+r>>1; Build(x<<1,l,Mid);Build(x<<1|1,Mid+1,r); Merge(g[x<<1],g[x<<1|1],g[x],x); } inline db Work(int x,db w){ int l=0,r=(int)g[x].size()-1,Ans=r; while(l<=r){ int Mid=l+r>>1; if(g[x][Mid].r>w+Eps)r=Mid-1,Ans=Mid;else l=Mid+1; } return a[g[x][Ans].id].Get(w); } void Query(int x,int l,int r,int L,int R,int y,int z){ if(l>R||r<L)return; if(l>=L&&r<=R){ Ans=max(Ans,Work(x,y)); Ans=max(Ans,Work(x,z)); return; } int Mid=l+r>>1; Query(x<<1,l,Mid,L,R,y,z);Query(x<<1|1,Mid+1,r,L,R,y,z); } int main(){ Read(n); for(int i=1;i<=n;i++){ Read(p);Read(x);Read(y); a[i].a=(db)y/(2ll*x*p-1ll*p*p-1ll*x*x); a[i].b=-a[i].a*2*x; a[i].c=-a[i].a*p*p-a[i].b*p; a[i].x=x;a[i].y=y;a[i].p=p; mx=max(mx,x*2-p); } build(1,1,n); Build(1,1,n); Read(m); while(m--){ Read(x);Read(y);Read(X);Read(Y); Ans=query(1,1,n,x,y,X,Y); Query(1,1,n,x,y,X,Y); printf("%.5lf\n",Ans); } return 0; }