Solution
f
[
i
]
=
m
i
n
(
f
[
j
]
+
(
i
−
j
−
1
+
s
u
m
[
i
]
−
s
u
m
[
j
]
−
L
)
2
)
f[i]=min(f[j]+(i-j-1+sum[i]-sum[j]-L)^2)
f[i]=min(f[j]+(i−j−1+sum[i]−sum[j]−L)2)
为了方便计算,我们定义
a
[
i
]
=
i
+
s
u
m
[
i
]
a[i]=i+sum[i]
a[i]=i+sum[i] ,
b
[
i
]
=
i
+
s
u
m
[
i
]
+
1
+
L
b[i]=i+sum[i]+1+L
b[i]=i+sum[i]+1+L
上式可转变为 f [ i ] = m i n ( f [ j ] + ( a [ i ] − b [ j ] ) 2 ) f[i]=min(f[j]+(a[i]-b[j])^2) f[i]=min(f[j]+(a[i]−b[j])2)
因为 i i i 是通过 j j j 转移而得到的,假设 j j j 为最优解 f [ i ] = f [ j ] + ( a [ i ] − b [ j ] ) 2 f[i]=f[j]+(a[i]-b[j])^2 f[i]=f[j]+(a[i]−b[j])2
f [ i ] = f [ j ] + a [ i ] 2 + b [ j ] 2 − 2 a [ i ] ⋅ b [ j ] f[i]=f[j]+a[i]^2+b[j]^2-2a[i]\cdot b[j] f[i]=f[j]+a[i]2+b[j]2−2a[i]⋅b[j]
上式除了 f [ i ] f[i] f[i] 均为已知,我们将与 i i i 相关的整理到一起,得到 f [ j ] + b [ j ] 2 = 2 a [ i ] ⋅ b [ j ] + f [ i ] − a [ i ] 2 f[j]+b[j]^2=2a[i]\cdot b[j]+f[i]-a[i]^2 f[j]+b[j]2=2a[i]⋅b[j]+f[i]−a[i]2
此时可以理解为 f [ j ] + b [ j ] 2 f[j]+b[j]^2 f[j]+b[j]2 为因变量 ( y ) (y) (y), b [ j ] b[j] b[j] 为自变量 ( x ) (x) (x),则 2 ⋅ a [ i ] 2\cdot a[i] 2⋅a[i] 为斜率。而想要 f [ i ] f[i] f[i] 最大,也就是截距最大。
那么我们定义点为 ( b [ i ] , f [ i ] + b [ i ] 2 ) (b[i],f[i]+b[i]^2) (b[i],f[i]+b[i]2)。想要截距最大,也就与次斜率相交最近的点即为答案。
所以我们要维护一个凸包(画图好麻烦啊…感性理解一波)
做法就是根据斜率,维护凸包。
首先看第一段斜率是否小于新加进来的这条的斜率,如果是,则把第一条便删去;
然后看最后一段斜率是不是大于新加进来的和倒数第二个点的斜率,如果是,则删去最后一个点。
注意:删点前提是有至少两个点。
注:前提是斜率单增…不然就需要二分了
Code
#include <cstdio>
#define ll long long
#define db double
const int N=50010;
int n,L,q[N];
db sum[N],c[N],f[N];
inline db a(int i){return i+sum[i];}
inline db b(int i){return a(i)+1+L;}
inline db X(int i){return b(i);}
inline db Y(int i){return f[i]+b(i)*b(i);}
inline db slope(int i,int j){return (Y(i)-Y(j))/(X(i)-X(j));}
int main(){
freopen("bzoj1010.in","r",stdin);
// freopen("a.out","w",stdout);
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++) {
scanf("%lf",&c[i]);
sum[i]=sum[i-1]+c[i];
}
int h=1,t=1;
for(int i=1;i<=n;i++){
while(h<t && slope(q[h],q[h+1])<2*a(i)) h++;
f[i]=f[q[h]]+(a(i)-b(q[h]))*(a(i)-b(q[h]));
// printf("%lld %lld\n",X[i],Y[i]);
while(h<t && slope(q[t],q[t-1])>slope(q[t-1],i)) t--;
q[++t]=i;
// printf("%d %lld\n",i,f[i]);
}
printf("%.0lf\n",f[n]);
return 0;
}