突然发现竟然还没做过这题,可能是不太会治分QDC吧
首先考虑n^2的dp
方程随便推一推就出来了
f[i]=max(f[i-1],f[j]*(r[j]*a[i]+b[i])/(r[j]*a[j]+b[j]))
60分到手
然后令y[i]=f[i]/(r[i]*a[i]+b[i]),x[i]=y[i]*r[i]。
然后会得到f[i]=max(x[j]*a[i]+y[j]*b[i])
这个很像点积啊
假如我们得到了i前面的点构成的凸包,就可以利用一种类似旋转卡壳的方法求出f[i]了
于是我们可以用平衡树维护动态凸包
其实很简单,大概也就几百行代码,反正我考场上肯定码不出来/调不出来
但是我们有更简单的做法,治分QDC
对于区间[l,r],我们总是保持它已经按照求凸包的顺序排序好了。
显然当[l,mid]和[mid+1,r]排序好之后我们可以利用归并排序[l,r]
同时假设[mid+1,r]中的标号全部大于[l,mid]中的标号
那么[mid+1,r]中的答案可以被[l,mid]中的更新,剩下的递归处理即可
然后这就是一道计算几何的题了
(话说这样是不是就能求动态凸包了)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const double eps=1e-9; const double inf=1e20; const int N=100000+5; int dcmp(double x){ if(fabs(x)<eps)return 0; return x<0?-1:1; } struct Query{ double x,y,a,b,r; int id; bool operator < (const Query &rhs)const{ if(!dcmp(x-rhs.x))return y<rhs.y; return x<rhs.x; } }q[N],aux[N]; bool cmp(Query i,Query j){ return i.a*j.b<i.b*j.a; } double cross(Query a,Query b,Query c){ return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } double dot(Query a,Query b){ return a.x*b.a+a.y*b.b; } double f[N]; int s[N]; void cdq(int l,int r){ if(l==r){ f[l]=max(f[l],f[l-1]); q[l].y=f[l]/(q[l].r*q[l].a+q[l].b); q[l].x=q[l].y*q[l].r; return; } int mid=l+r>>1; int t1=l,t2=mid+1; for(int i=l;i<=r;i++) if(q[i].id<=mid)aux[t1++]=q[i]; else aux[t2++]=q[i]; for(int i=l;i<=r;i++) q[i]=aux[i]; cdq(l,mid); int top=0; for(int i=l;i<=mid;i++){ while(top>1&&dcmp(cross(q[s[top-1]],q[s[top]],q[i]))>=0)top--; s[++top]=i; } int j=1; for(int i=mid+1;i<=r;i++){ while(j<top&&dcmp(dot(q[s[j+1]],q[i])-dot(q[s[j]],q[i]))>=0)j++; f[q[i].id]=max(f[q[i].id],dot(q[s[j]],q[i])); } cdq(mid+1,r); t1=l,t2=mid+1; for(int i=l;i<=r;i++) if(t1<=mid&&(t2>r||q[t1]<q[t2]))aux[i]=q[t1++]; else aux[i]=q[t2++]; for(int i=l;i<=r;i++)q[i]=aux[i]; } int main(){ //freopen("a.in","r",stdin); int n;scanf("%d%lf",&n,&f[0]); for(int i=1;i<=n;i++){ scanf("%lf%lf%lf",&q[i].a,&q[i].b,&q[i].r); q[i].id=i; } sort(q+1,q+1+n,cmp); cdq(1,n); printf("%.3lf\n",f[n]); return 0; }