题目
描述 Description
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1…N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过L。但他希望费用最小.
输入格式 Input Format
第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
输出格式 Output Format
输出最小费用
样例输入 Sample Input
5 4
3
4
2
1
4
样例输出 Sample Output
1
题解
- 首先显然的
D
P
DP
DP式
f [ i ] = min ( f [ j ] + ( s u m [ i ] + i − s u m [ j ] − j − L − 1 ) 2 ) f[i] = \min(f[j] + (sum[i] + i - sum[j] - j - L - 1)^2) f[i]=min(f[j]+(sum[i]+i−sum[j]−j−L−1)2) - f [ i ] f[i] f[i]表示选到第 i i i个玩具所能得到的最小价值, s u m [ i ] sum[i] sum[i]为前缀和
- 那么这种类型的方程式无疑需要
O
(
n
2
)
O(n^2)
O(n2)枚举,我们来想办法优化一下,我们设
a [ i ] = s u m [ i ] + i , b [ i ] = s u m [ i ] + i + L + 1 a[i] = sum[i] + i,b[i] = sum[i] + i + L + 1 a[i]=sum[i]+i,b[i]=sum[i]+i+L+1 - 那么方程就变为了
f [ i ] = min ( 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) - 将方程展开就变为了
f [ i ] = min ( f [ j ] + a [ i ] 2 − 2 ∗ a [ i ] ∗ b [ j ] + b [ j ] 2 ) f[i] = \min (f[j] + a[i] ^ 2 - 2*a[i]*b[j] + b[j]^2) f[i]=min(f[j]+a[i]2−2∗a[i]∗b[j]+b[j]2) - 令
1
⩽
x
,
y
<
i
1 \leqslant x, y < i
1⩽x,y<i, 且
f
[
x
]
<
f
[
y
]
f[x] < f[y]
f[x]<f[y],即
x
x
x比
y
y
y优,那么我们便有
f [ x ] + ( a [ i ] − b [ x ] ) 2 ⩽ f [ y ] + ( a [ i ] − b [ y ] ) 2 f[x]+(a[i]-b[x])^2 \leqslant f[y] + (a[i]-b[y])^2 f[x]+(a[i]−b[x])2⩽f[y]+(a[i]−b[y])2 - 展开得
f [ x ] + a [ i ] 2 − 2 ∗ a [ i ] ∗ b [ x ] + b [ x ] 2 ⩽ f [ y ] + a [ i ] 2 − 2 ∗ a [ i ] ∗ b [ y ] + b [ y ] 2 f[x]+a[i]^2 - 2*a[i]*b[x]+b[x]^2 \leqslant f[y] + a[i] ^ 2 - 2 * a[i] * b[y] + b[y] ^ 2 f[x]+a[i]2−2∗a[i]∗b[x]+b[x]2⩽f[y]+a[i]2−2∗a[i]∗b[y]+b[y]2 - 化简得
( f [ x ] − f [ y ] ) + ( b [ x ] 2 − b [ y ] 2 ) b [ x ] − b [ y ] ⩽ 2 ∗ a [ i ] \frac{(f[x]-f[y]) + (b[x] ^ 2 - b[y] ^ 2)}{b[x] - b[y]} \leqslant 2*a[i] b[x]−b[y](f[x]−f[y])+(b[x]2−b[y]2)⩽2∗a[i] - 那么也就是说如果有 x , y x,y x,y满足上面的式子便有 f [ x ] < f [ y ] f[x] < f[y] f[x]<f[y]
- 如果我们把 f [ i ] + b [ i ] 2 f[i] + b[i]^2 f[i]+b[i]2当成纵坐标, b [ i ] b[i] b[i]看做横坐标
- 那么其实就可以化为 Δ x Δ y \frac{\Delta x}{\Delta y} ΔyΔx,即一条直线的斜率 k k k
- 当一条直线的斜率 ⩽ 2 ∗ a [ i ] \leqslant 2*a[i] ⩽2∗a[i]就说明 x x x的值比 y y y优
- 因为我们的 a [ i ] a[i] a[i]是单调递增的,那么我们便可以用单调队列来进行维护
- 具体维护过程为将决策插进单调队列中,将队头与当前点 i i i进行比较,如果当前点与对头之间的斜率比 f [ i ] f[i] f[i]小就把队首删去,对于队尾来说把每次加入 i i i后不满足单调递增的全部删去,再把 i i i插入队尾即可
code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 100;
template <typename T>
inline void read(T &s) {
s = 0;
T w = 1, ch = getchar();
while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
s *= w;
}
int n, l, head, tail;
int q[maxn];
double sum[maxn], f[maxn];
inline double a(int x) { return sum[x] + x; }
inline double b(int x) { return sum[x] + x + l + 1; }
inline double c(int x) { return b(x) * b(x); }
inline double slope(int x, int y) { return (f[x] + c(x) - f[y] - c(y)) / (b(x) - b(y)); }
int main() {
read(n); read(l);
for (int i = 1; i <= n; ++i) {
scanf("%lf", &sum[i]);
sum[i] += sum[i - 1];
}
head = tail = 1;
for (int i = 1; i <= n; ++i) {
while (head < tail && slope(q[head], q[head + 1]) <= 2 * a(i)) ++head;
f[i] = f[ q[head] ] + (a(i) - b(q[head])) * (a(i) - b(q[head]));
while (head < tail && slope(i, q[tail - 1]) < slope(q[tail - 1], q[tail])) --tail;
q[++tail] = i;
}
printf("%lld\n", (long long)f[n]);
return 0;
}