原题见洛谷对应题号的题面。
分析这道题,我们能得到一个初步的DP方程,即:
f
[
i
]
=
m
i
n
{
f
[
j
]
+
(
Σ
j
+
1
i
c
i
+
i
−
j
−
1
−
L
)
2
}
,
1
<
=
j
<
i
f[i]=min\{f[j]+(\Sigma^i_{j+1}c_i+i-j-1-L)^2\},1<=j<i
f[i]=min{f[j]+(Σj+1ici+i−j−1−L)2},1<=j<i
计算这个方程的复杂度显然是
O
(
n
3
)
O(n^3)
O(n3)的,因此我们需要对其进行优化。
设
s
[
i
]
s[i]
s[i]为
c
[
i
]
c[i]
c[i]的前缀和数组,
m
(
i
)
=
s
[
i
]
+
i
m(i)=s[i]+i
m(i)=s[i]+i,为了书写方便,去掉min,则我们可以把上面的方程改写为这样的形式:
f
[
i
]
=
f
[
j
]
+
(
s
[
i
]
−
s
[
j
]
+
i
−
j
−
1
−
L
)
2
f[i]=f[j]+(s[i]-s[j]+i-j-1-L)^2
f[i]=f[j]+(s[i]−s[j]+i−j−1−L)2
然后有:
f
[
i
]
=
f
[
j
]
+
[
(
m
(
i
)
−
m
(
j
)
)
−
(
L
+
1
)
]
2
f[i]=f[j]+[(m(i)-m(j))-(L+1)]^2
f[i]=f[j]+[(m(i)−m(j))−(L+1)]2
f
[
i
]
=
f
[
j
]
+
m
(
i
)
2
+
m
(
j
)
2
−
2
∗
m
(
i
)
∗
m
(
j
)
+
(
L
+
1
)
2
−
2
∗
(
m
(
i
)
−
m
(
j
)
)
∗
(
L
+
1
)
f[i]=f[j]+m(i)^2+m(j)^2-2*m(i)*m(j)+(L+1)^2-2*(m(i)-m(j))*(L+1)
f[i]=f[j]+m(i)2+m(j)2−2∗m(i)∗m(j)+(L+1)2−2∗(m(i)−m(j))∗(L+1)
f
[
i
]
=
f
[
j
]
+
m
(
j
)
2
+
m
(
i
)
2
−
2
∗
m
(
i
)
∗
m
(
j
)
−
2
∗
(
L
+
1
)
∗
m
(
i
)
+
2
∗
(
L
+
1
)
∗
m
(
j
)
+
(
L
+
1
)
2
f[i]=f[j]+m(j)^2+m(i)^2-2*m(i)*m(j)-2*(L+1)*m(i)+2*(L+1)*m(j)+(L+1)^2
f[i]=f[j]+m(j)2+m(i)2−2∗m(i)∗m(j)−2∗(L+1)∗m(i)+2∗(L+1)∗m(j)+(L+1)2
f
[
i
]
=
f
[
j
]
+
m
(
j
)
2
+
[
−
2
∗
m
(
i
)
+
2
∗
L
+
2
]
∗
m
(
j
)
+
[
m
(
i
)
−
2
∗
L
−
2
]
∗
m
(
i
)
+
(
L
+
1
)
2
f[i]=f[j]+m(j)^2+[-2*m(i)+2*L+2]*m(j)+[m(i)-2*L-2]*m(i)+(L+1)^2
f[i]=f[j]+m(j)2+[−2∗m(i)+2∗L+2]∗m(j)+[m(i)−2∗L−2]∗m(i)+(L+1)2
移项,有:
f
[
j
]
+
m
(
j
)
2
=
[
2
∗
m
(
i
)
−
2
∗
L
−
2
]
∗
m
(
j
)
+
[
2
∗
L
+
2
−
m
(
i
)
]
∗
m
(
i
)
−
(
L
+
1
)
2
+
f
[
i
]
f[j]+m(j)^2=[2*m(i)-2*L-2]*m(j)+[2*L+2-m(i)]*m(i)-(L+1)^2+f[i]
f[j]+m(j)2=[2∗m(i)−2∗L−2]∗m(j)+[2∗L+2−m(i)]∗m(i)−(L+1)2+f[i]
至此,我们观察这个式子,可以发现,设:
f
[
j
]
+
m
(
j
)
2
=
y
,
[
2
∗
m
(
i
)
−
2
∗
L
−
2
]
=
k
,
m
(
j
)
=
x
,
[
2
∗
L
+
2
−
m
(
i
)
]
∗
m
(
i
)
−
(
L
+
1
)
2
+
f
[
i
]
=
b
f[j]+m(j)^2=y,[2*m(i)-2*L-2]=k,m(j)=x,[2*L+2-m(i)]*m(i)-(L+1)^2+f[i]=b
f[j]+m(j)2=y,[2∗m(i)−2∗L−2]=k,m(j)=x,[2∗L+2−m(i)]∗m(i)−(L+1)2+f[i]=b
则有
y
=
k
x
+
b
y=kx+b
y=kx+b,其中
b
b
b 含有我们需要求解的
f
[
i
]
f[i]
f[i]。
我们的目的是让 f [ i ] f[i] f[i]取得最小值,则 b b b 就应取得最小值。我们可以将这个问题转换成直线在平面直角坐标系内的纵截距最小的问题。这样,这个问题就与线性规划问题非常相似了。
在枚举
i
i
i 时,我们每次都能得到一个固定的斜率
k
k
k (在后面称为“标准斜率”),标准斜率即为在
i
=
k
i=k
i=k 时,理想状态下的答案,且斜率小于它的直线是不合法,也就是在现在的条件下无法成立的。因此,我们可以维护一个
k
k
k 值单调递减的单调队列,每次将不合法的斜率出队,取比标准斜率大的第一个斜率,也就是队头,作为计算答案时使用的斜率。同时,检查入队的点与在它之前的点组成的斜率的关系,来判断队尾的点是否应该出队。
如图,
k
k
k 为标准斜率,
k
1
k_1
k1和
k
2
k_2
k2 为“入队的点与在它之前的点组成的斜率”,入队的点是所对应的
x
x
x 值最大的点。在左图中,
k
1
>
k
2
k_1>k_2
k1>k2 ,所以
k
1
k_1
k1 仍然是最优解,而
k
2
k_2
k2 虽然没有
k
1
k_1
k1 优,但是组成它的点在组成
k
1
k_1
k1 的点之后,仍然有成为最优解的潜力。在右图中,
k
1
<
k
2
k_1<k_2
k1<k2 ,因此在
k
1
k_1
k1和
k
2
k_2
k2 中间的点一定不可能参与构成最优解,因为
k
3
k_3
k3 一定比
k
2
k_2
k2 更优。这时,我们就可以删除在
k
1
k_1
k1和
k
2
k_2
k2 中间的点,也就是上面提到的“出队”,之后再把现在所需的点入队。
这样,一定能保证每次计算出的答案都是最优的。
关于两点之间的斜率,我们可以直接使用斜率公式 ( y 1 − y 2 ) / ( x 1 − x 2 ) (y_1-y_2)/(x_1-x_2) (y1−y2)/(x1−x2)来求解,并与标准斜率作比较。
AC代码:
#include<cstdio>
#define ll long long
using namespace std;
ll n,l,q[50005],tail=1,head=1,f[50005],s[50005];
ll m(ll j) {return s[j]+j;}
ll y(ll j) {return f[j]+m(j)*m(j);}
ll k(ll i) {return 2*m(i)-2*l-2;}
long double nk(ll i,ll j) {return (y(i)-y(j))/(m(i)-m(j));}
ll b(ll i) {return m(i)*(2*l+2-m(i))-(l+1)*(l+1);}
int main()
{
scanf("%lld%lld",&n,&l);
for(int i=1;i<=n;i++)
{
scanf("%lld",&s[i]);
s[i]+=s[i-1];
}
for(int i=1;i<=n;i++)
{
while(head<tail&&k(i)>nk(q[head],q[head+1])) head++;
f[i]=y(q[head])-k(i)*m(q[head])-b(i);
while(head<tail&&nk(q[tail-1],i)<nk(q[tail-1],q[tail])) tail--;
q[++tail]=i;
}
printf("%lld",f[n]);
return 0;
}