Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

P3195 [HNOI2008]玩具装箱

P3195 [HNOI2008]玩具装箱

先推出转移方程
$$
f _ i = \min _ {j < i} \{ f _ j + (s _ i - s _ j + i - j - 1 - L) ^ 2 \}
$$
为了方便,令
$$
s _ i = s _ i +i
$$

$$
L = L + 1
$$

那么方程为
$$
f _ i = \min \{ f _ j + (s _ i - s _ j - L) ^ 2 \}
$$
化为斜率优化的形式
$$
f _ i - (s _i - L) ^ 2 = \min _ {j < i} \{ f _ j + s _ j ^ 2 + 2 s _ j (L - s _ i)\}
$$
因为 $s _ i$ 单增,$s _ i + i$ 也是单增,用单调队列维护这个凸包即可。

查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 5e4 + 10, inf = 1e9;
int hd = 1, tl, q[N];
int n, L, w[N];
LL s[N], f[N];
double slope(int a, int b)
{
return (double)(f[a] + s[a] * s[a] - f[b] - s[b] * s[b]) / (double)(s[a] - s[b]);
}
int main()
{
scanf("%d%d", &n, &L);
for (int i = 1; i <= n; i++)
scanf("%d", &w[i]);
for (int i = 1; i <= n; i++)
s[i] = s[i - 1] + w[i];
for (int i = 1; i <= n; i++)
s[i] += i;
L++;
q[++tl] = 0;
for (int i = 1; i <= n; i++)
{
while (hd < tl && slope(q[hd], q[hd + 1]) < 2 * (s[i] - L))
hd++;
f[i] = f[q[hd]] + (s[i] - s[q[hd]] - L) * (s[i] - s[q[hd]] - L);
while (hd < tl && slope(q[tl], q[tl - 1]) > slope(i, q[tl]))
tl--;
q[++tl] = i;
}
printf("%lld", f[n]);
return 0;
}