【题目描述】
你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 n 编号,要将他们拆分 成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号 应该连续,即为形如
(
i
,
i
+
1
,
.
.
.
,
i
+
k
)
(i, i + 1, ..., i + k)
(i,i+1,...,i+k)的序列。 编号为 i 的士兵的初始战斗力为 xi ,一支特别行动队的初始战斗力 x 为队内 士兵初始战斗力之和,
即
x
i
+
x
i
+
1
+
.
.
.
+
x
i
+
k
x_i +x_{i+1} + ... + x_{i+k}
xi+xi+1+...+xi+k 。
通过长期的观察,你总结出一支特别行动队的初始战斗力 x 将按如下经验公 式修正为 x ′ : x ′ = a x 2 + b x + c x':x'= ax^2+bx+c x′:x′=ax2+bx+c,其中 a, b, c 是已知的系数(a < 0)。 作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队修正后 战斗力之和最大。试求出这个最大和。
例如,你有 4 名士兵, x 1 = 2 , x 2 = 2 , x 3 = 3 , x 4 = 4 x_1 = 2, x_2 = 2, x_3 = 3, x_4 = 4 x1=2,x2=2,x3=3,x4=4。经验公式中的参数为 a = – 1 , b = 10 , c = – 20 a = –1, b = 10, c = –20 a=–1,b=10,c=–20。此时,最佳方案是将士兵组成 3 个特别行动队:第一队包含士兵 1 和士兵 2,第二队包含士兵 3,第三队包含士兵 4。特别行动队的初始战斗力分 别为 4, 3, 4,修正后的战斗力分别为 4, 1, 4。修正后的战斗力和为 9,没有其它 方案能使修正后的战斗力和更大。
【输入格式】
输入由三行组成。第一行包含一个整数 n,表示士兵的总数。第二行包含三 个整数 a, b, c,经验公式中各项的系数。第三行包含 n 个用空格分隔的整数 x 1 , x 2 , … , x n x_1, x_2, …, x_n x1,x2,…,xn,分别表示编号为 1 , 2 , … , n 1, 2, …, n 1,2,…,n 的士兵的初始战斗力。
【输出格式】
输出一个整数,表示所有特别行动队修正后战斗力之和的最大值。
S a m p l e I n p u t Sample~~Input Sample Input
4
-1 10 -20
2 2 3 4
S a m p l e O u t p u t Sample~~Output Sample Output
9
【题意分析】
正常的OI选手都可以推出的暴力dp:
d p i = max 1 ≤ j < i { d p j + a ( s i − s j ) 2 + b ( s i − s j ) + c } dp_i=\max_{1\leq j < i}{\{dp_j+a(s_i-s_j)^2+b(s_i-s_j)+c \}} dpi=1≤j<imax{dpj+a(si−sj)2+b(si−sj)+c}
其中s[i]
表示前缀和。
那么显然只能拿到20pts,这个式子具有很明显的斜率优化特征。
根据斜率优化套路假设 x x x这个决策比 y y y更优。(假设 x > y x>y x>y并且下面的 s l o p e ( x , y ) slope(x,y) slope(x,y)中x也比y大)
令j分别等于x和y,暴展得到:
d p x + a ( s i − s x ) 2 + b ( s i − s x ) + c > d p y + a ( s i − s y ) 2 + b ( s i − s y ) + c dp_x+a(s_i-s_x)^2+b(s_i-s_x)+c>dp_y+a(s_i-s_y)^2+b(s_i-s_y)+c dpx+a(si−sx)2+b(si−sx)+c>dpy+a(si−sy)2+b(si−sy)+c
⇒ ( d p x + a s x ) 2 − ( d p y + a s y ) 2 s x − s y > 2 a s i + b \Rightarrow \frac{(dp_x+as_x)^2-(dp_y+as_y)^2}{s_x-s_y}>2as_i+b ⇒sx−sy(dpx+asx)2−(dpy+asy)2>2asi+b
这样根据 a < 0 a<0 a<0这个条件,不等式右侧单调递减,两边同乘-1
⇒ − ( d p x + a s x ) 2 − ( d p y + a s y ) 2 s x − s y < − 2 a s i − b \Rightarrow - \frac{(dp_x+as_x)^2-(dp_y+as_y)^2}{s_x-s_y}<-2as_i-b ⇒−sx−sy(dpx+asx)2−(dpy+asy)2<−2asi−b
这样成功地可以进行 s l o p e slope slope维护一个下凸包
就到这里吧
Code:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
#define MAXN 1000010
#define db double
using namespace std;
int sum[MAXN], dp[MAXN], q[MAXN], n, a, b, c;
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 sqr (int x) {return x * x;}
inline int f (int x) {return a * sqr (x) + b * x + c;}
inline int g (int x) {return dp[x] + a * sqr (sum[x]);}
inline db slope (int x, int y) {return -1.0 * (g (x) - g (y)) / (sum[x] - sum[y]);}
signed main () {
n = read (), a = read (), b = read (), c = 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]) < - (2 * a * sum[i] + b)) h++;
dp[i] = dp[q[h]] + f (sum[i] - sum[q[h]]);
while (h < t && slope (q[t], q[t - 1]) > slope (i, q[t])) t--;
q[++t] = i;
}
return printf ("%lld\n", dp[n]), 0;
}