【题目描述】
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为 1 ⋯ N 1\cdots N 1⋯N 的 N N N 件玩具,第 i i i 件玩具经过压缩后变成一维长度为 C i C_i Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第 i i i 件玩具到第 j j j 个玩具放到一个容器中,那么容器的长度将为 x = j − i + ∑ k = i j C k x=j-i+\sum\limits_{k=i}^{j}C_k x=j−i+k=i∑jCk制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 x x x ,其制作费用为 ( X − L ) 2 (X-L)^2 (X−L)2.其中 L L L 是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 L L L 。但他希望费用最小.
【输入格式】
第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
【输出格式】
输出最小费用
S a m p l e I n p u t Sample~~Input Sample Input
5 4
3
4
2
1
4
S a m p l e O u t p u t Sample~~Output Sample Output
1
【题意分析】
做题前确保你已经读懂了题意。
令s[i]
为a[i]
前缀和,f[i]=s[i]+i
,g[i]=s[i]+i-L-1
暴力DP:
d p i = min 1 ≤ j < i { d p j + ( s i + i − s j − j − L − 1 ) 2 } dp_i=\min _{1\leq j< i} \{ dp_j +(s_i+i-s_j-j-L-1)^2\} dpi=1≤j<imin{dpj+(si+i−sj−j−L−1)2}
⇒ d p i = min 1 ≤ j < i { d p j + ( g i − f j ) 2 } \Rightarrow dp_i=\min _{1\leq j< i} \{ dp_j+(g_i-f_j)^2\} ⇒dpi=1≤j<imin{dpj+(gi−fj)2}
瞬间炸裂,考虑斜率优化,令
x
,
y
x,y
x,y为两个决策且x比y大。
那么得到这个柿子:
d p x + ( g i − f x ) 2 < d p y + ( g i − f y ) 2 dp_x+(g_i-f_x)^2<dp_y+(g_i-f_y)^2 dpx+(gi−fx)2<dpy+(gi−fy)2
⇒ ( d p x + f x 2 ) − ( d p y + f y 2 ) f x − f y < 2 g i \Rightarrow \frac{(dp_x+f_x^2)-(dp_y+f_y^2)}{f_x-f_y}<2g_i ⇒fx−fy(dpx+fx2)−(dpy+fy2)<2gi
快乐斜率优化即可。
Code:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#define MAXN 1000010
#define db double
#define int long long
using namespace std;
int sum[MAXN], q[MAXN], dp[MAXN], n, L;
inline int read () {
register int s = 0, w = 1;
register char ch = getchar ();
while (! isdigit (ch)) {if (ch == '-') w = -1; ch = getchar ();}
while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
return s * w;
}
inline int f (int x) {return x + sum[x];}
inline int g (int x) {return x + sum[x] - 1 - L;}
inline int sqr (int x) {return x * x;}
inline db slope (int x, int y) {return 1.0 * ((dp[x] + sqr (f (x))) - (dp[y] + sqr (f (y)))) / (f (x) - f (y));}
signed main () {
n = read (), L = read ();
for (register int i = 1; i <= n; i++) sum[i] = sum[i - 1] + read ();
int h = 0, t = 0;
for (register int i = 1; i <= n; i++) {
while (h < t && slope (q[h + 1], q[h]) < g (i) * 2) h++;
dp[i] = dp[q[h]] + sqr (sum[i] + i - sum[q[h]] - q[h] - L - 1);
while (h < t && slope (q[t], q[t - 1]) > slope (i, q[t])) t--;
q[++t] = i;
}
return printf ("%lld\n", dp[n]), 0;
}