首先是愉快的推式子
$dp[i]=max(dp[i],frac{sum[i]-sum[j-1]}{x[i]+(i-j)*d})(1<=j<=i<=n)$(考虑有一只僵尸正好走到门前被打死,这样最优)
然后发现这个玩意好像和平时的斜率优化不太一样,好像这个式子自己就是一个斜率,我们把它分成和$i$,$j$有关的两坨,这样清楚一点
$dp[i]=max(dp[i],frac{(sum[i])-(sum[j-1])}{(x[i]+d*i)-(d*j)})$
这样很清楚了,对于每个$i$我们就是在找$(sum[i],x[i]+d*i)$这个点向所有$1<=j<=i$连出的直线中的斜率的最大值,于是维护一个凸包并在上面二分即可
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=100005; 6 const double eps=1e-7; 7 int n,top,stk[N]; 8 double d,ans,sta[N],sum[N]; 9 inline double x(int p){return d*p;} 10 inline double y(int p){return sum[p-1];} 11 inline double s(int p,int q){return (y(q)-y(p))/(x(q)-x(p));} 12 inline double X(int p){return sta[p]+d*p;} 13 inline double Y(int p){return sum[p];} 14 inline double S(int p,int q){return (Y(q)-y(p))/(X(q)-x(p));} 15 int main() 16 { 17 scanf("%d%lf",&n,&d); 18 for(int i=1;i<=n;i++) 19 scanf("%lf%lf",&sum[i],&sta[i]),sum[i]+=sum[i-1]; 20 for(int i=1;i<=n;i++) 21 { 22 while(top>1&&s(stk[top-1],stk[top])>s(stk[top],i)) top--; 23 stk[++top]=i; 24 int l=1,r=top,tmp=0; 25 while(l<=r) 26 { 27 int mid=(l+r)/2; 28 if(S(stk[mid],i)>S(stk[mid-1],i)) l=mid+1,tmp=mid; 29 else r=mid-1; 30 } 31 ans+=S(stk[tmp],i); 32 } 33 printf("%d",(int)(ans+0.5)); 34 return 0; 35 }