题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3203
解法:
本题读完题目,我们可以很快知道,对于第i轮,yi的最小值就是max(sum[i]-sum[j-1])/(xi+(i-j)*d),1<=j<=i。其中sum[i]=sigma(a[j]),1<=j<=i。
这样我们可以得到一个n^2的算法。
观察式子,我们可以发现,它的集合意义就是P(sum[i],x[i]+i*d)到所有Q(sum[j-1],j*d)连线的最大值。
显然Q,P都是定点。接下来我们看怎么才能取到斜率最大呢?。很显然的一个结论是取到max的点在Q的凸壳上。
如图,kPQ'<kPQ,我们总是可以把凸壳内的点拉到凸壳上获得更大的斜率= =。
接着就是在凸壳上求最大斜率,又有个很显然的结论就是凸壳上到P的斜率是个单峰函数。如下图:
kPQ1<kPQ2<kPQ3,kPQ3>kPQ4>kPQ5。。是不是很显然
然后我们三分就好了。就可以算出最大斜率了。
代码:(这是BZOJ上目前rank1的)
/************************************************************** Problem: 3203 User: hta Language: C++ Result: Accepted Time:216 ms Memory:4396 kb ****************************************************************/ #include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <string> using namespace std; const int maxn=100003,lim=0; const double eps=1e-18; typedef long long LL; int n; double d,a[maxn],x[maxn],ans=0; struct Tpoint { double x,y; Tpoint(){} Tpoint(double _x,double _y){x=_x,y=_y;} Tpoint operator -(const Tpoint &b)const{return Tpoint(x-b.x,y-b.y);} double operator *(const Tpoint &b)const{return x*b.y-y*b.x;} }s[maxn]; inline LL get() { LL f=0,v=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-')break; if(ch=='-')f=1;else v=ch-48; while(isdigit(ch=getchar()))v=v*10+ch-48; if(f==1)return -v;else return v; } inline int sig(double x){return fabs(x)<=eps?0:(x>eps?1:-1);} inline double slope(const Tpoint &a,const Tpoint &b){return (a.y-b.y)/(a.x-b.x);} int main() { n=get(),d=get(); for(int i=1;i<=n;i++)a[i]=get(),x[i]=get(); double sum=0; int top=0; for(int i=1;i<=n;i++) { Tpoint p=Tpoint(i*d,sum);sum+=a[i]; while(top>1&&sig((p-s[top-1])*(s[top]-s[top-1]))>=0)top--; s[++top]=p; p=Tpoint(x[i]+i*d,sum); int l=1,r=top,m1,m2; while(r-l>=3) { m1=l+(r-l)/3,m2=r-(r-l)/3; double k1=slope(s[m1],p),k2=slope(s[m2],p); if(k1<k2)l=m1;else r=m2; } double res=0; for(int j=l;j<=r;j++)res=max(res,slope(s[j],p)); ans+=res; } printf("%.0lf\n",ans); return 0; }