解题:SDOI 2013 保护出题人

题面

首先是愉快的推式子

$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 }
View Code
原文地址:https://www.cnblogs.com/ydnhaha/p/10006682.html