1. 程式人生 > >解題:SDOI 2013 保護出題人

解題: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