题目描述
分析
先吐槽一波,为什么 latex 不能复制!!!!
好了让我们看看这道题。
设
s
i
s_i
si 为前
i
i
i 个玩具的长度前缀和,设
f
i
f_i
fi 表示前
i
i
i 个玩具的最小费用,于是有一个显然的转移方程:
f
i
=
m
i
n
{
f
j
−
1
+
(
i
−
j
+
s
i
−
s
j
−
1
−
L
)
2
}
f_i=min\{f_{j-1}+(i-j+s_i-s_{j-1}-L)^2\}
fi=min{fj−1+(i−j+si−sj−1−L)2}
然后我们令
g
(
x
)
=
s
x
+
x
−
L
g(x)=s_x+x-L
g(x)=sx+x−L,
t
(
x
)
=
x
+
s
x
−
1
t(x)=x+s_{x-1}
t(x)=x+sx−1
可以将转移方程化简为:
f
i
=
m
i
n
{
f
j
−
1
+
t
(
j
)
2
−
2
g
(
i
)
t
(
j
)
}
+
g
(
i
)
2
f_i=min\{f_{j-1}+t(j)^2-2g(i)t(j)\}+g(i)^2
fi=min{fj−1+t(j)2−2g(i)t(j)}+g(i)2
好了,来到这里,要开始进入斜率优化了!
令
h
(
x
)
=
f
j
−
1
+
t
(
j
)
2
h(x)=f_{j-1}+t(j)^2
h(x)=fj−1+t(j)2
那么式子成为:
f
i
=
m
i
n
{
h
(
j
)
−
2
g
(
i
)
t
(
j
)
}
+
g
(
i
)
2
f_i=min\{h(j)-2g(i)t(j)\}+g(i)^2
fi=min{h(j)−2g(i)t(j)}+g(i)2
我们做这一切完全是为了让式子好看一些(雾
既然叫优化,我们就不可能枚举每个点去转移,而是要快速得到决策点。
考虑
j
1
<
j
2
j_1<j_2
j1<j2,
j
2
j_2
j2 比
j
1
j_1
j1 优的条件,注意下面中
t
(
j
2
)
>
t
(
j
1
)
t(j_2)>t(j_1)
t(j2)>t(j1),因为
t
(
x
)
t(x)
t(x) 单调增:
h
(
j
1
)
−
2
g
(
i
)
t
(
j
1
)
>
h
(
j
2
)
−
2
g
(
i
)
t
(
j
2
)
⇒
h
(
j
2
)
−
h
(
j
1
)
<
2
g
(
i
)
(
t
(
j
2
)
−
t
(
j
1
)
)
⇒
h
(
j
2
)
−
h
(
j
1
)
t
(
j
2
)
−
t
(
j
1
)
<
2
g
(
i
)
h(j_1)-2g(i)t(j_1)>h(j_2)-2g(i)t(j_2)\\\Rightarrow h(j_2)-h(j_1)<2g(i)(t(j_2)-t(j_1))\\\Rightarrow \frac{h(j_2)-h(j_1)}{t(j_2)-t(j_1)}<2g(i)
h(j1)−2g(i)t(j1)>h(j2)−2g(i)t(j2)⇒h(j2)−h(j1)<2g(i)(t(j2)−t(j1))⇒t(j2)−t(j1)h(j2)−h(j1)<2g(i)
那部分是不是很像斜率的形式?所以称之为斜率优化。
记
T
(
j
1
,
j
2
)
=
h
(
j
2
)
−
h
(
j
1
)
t
(
j
2
)
−
t
(
j
1
)
T(j_1,j_2)=\frac{h(j_2)-h(j_1)}{t(j_2)-t(j_1)}
T(j1,j2)=t(j2)−t(j1)h(j2)−h(j1)
我们目前得到的结论:若
j
1
<
j
2
j_1<j_2
j1<j2,
T
(
j
1
,
j
2
)
<
2
g
(
i
)
T(j_1,j_2)<2g(i)
T(j1,j2)<2g(i) 时,
j
2
j_2
j2 更优。
下面是两个重要的结论
-
队列中相邻点的斜率都大于 2 g ( i ) 2g(i) 2g(i) 时,决策点为队首。
因为 T ( j 1 , j 2 ) > 2 g ( i ) T(j_1,j_2)>2g(i) T(j1,j2)>2g(i),因此 j 1 j_1 j1 优于 j 2 j_2 j2,同理, j 2 j_2 j2 优于 j 3 j_3 j3,以此类推。 -
队列中相邻点斜率单调增
让我们考虑 j 1 < j 2 < j 3 j_1<j_2<j_3 j1<j2<j3 这相邻三个点的情况: j 2 j_2 j2 要成为决策点,就必须满足存在 g ( i ) g(i) g(i):- j 2 j_2 j2 比 j 1 j_1 j1 优,即 T ( j 1 , j 2 ) < 2 g ( i ) T(j_1,j_2)<2g(i) T(j1,j2)<2g(i)
- j 2 j_2 j2 比 j 3 j_3 j3 优,即 T ( j 2 , j 3 ) > 2 g ( i ) T(j_2,j_3)>2g(i) T(j2,j3)>2g(i)
于是有 T ( j 1 , j 2 ) < 2 g ( i ) < T ( j 2 , j 3 ) T(j_1,j_2)<2g(i)<T(j_2,j_3) T(j1,j2)<2g(i)<T(j2,j3),于是队列中斜率单调增。
知道了这个,就可以用一个单调队列来维护斜率了。
于是,我们用单调队列维护斜率单调增,同时队列中斜率都大于
2
g
(
i
)
2g(i)
2g(i),每次决策点在队首。
由于每个点只进出队列一次,所以总复杂度
O
(
n
)
O(n)
O(n)
代码如下
#include <bits/stdc++.h>
#define N 50005
using namespace std;
typedef long long LL;
LL z = 1, L, s[N], f[N];
int q[N], x = 1, y;
LL t(int x){
return s[x - 1] + x;
}
LL h(int x){
return f[x - 1] + t(x) * t(x);
}
LL g(int x){
return x + s[x] - L;
}
double T(int i, int j){
return 1.0 * (h(i) - h(j)) / (t(i) - t(j));
}
int main(){
int i, j, n, m;
scanf("%d%d", &n, &L);
for(i = 1; i <= n; i++) scanf("%d", &j), s[i] = s[i - 1] + j;
for(i = 1; i <= n; i++){
q[++y] = i;
while(x + 1 < y && T(q[y - 1], q[y]) < T(q[y - 2], q[y - 1])) q[y - 1] = q[y], y--;
while(x < y && T(q[x], q[x + 1]) < 2 * g(i)) x++;
f[i] = f[q[x] - 1] + (g(i) - t(q[x])) * (g(i) - t(q[x]));
}
printf("%lld", f[n]);
return 0;
}