一支部队由 名预备役士兵组成,士兵从 到 编号,要将他们拆分成若干特别行动队,同一队中队员的编号应该连续。
士兵 的初始战斗力为 一支特别行动队的初始战斗力 为各士兵初始战斗力之和。一支特别行动队的战斗力会被修正为 ,其中 、、 已知,。
求出将所有士兵组成若干特别行动队的最大总战斗力。
链接
题解
设 表示前 名士兵分成若干特别行动队的最大战斗力, 表示前缀和。
枚举 ,将第 到 个分在同一队里,状态转移方程为
时间复杂度为 ,超时,需要优化。
考虑两个决策点 、(),若 比 优,则有
不等式右边单调递减,左边分母上的前缀和单调递增。
用单调队列存储所有决策点,维护一个上凸壳,从左到后两两之间的斜率递减,且均小于当前的 ,每次最优决策从最左边取得。
时间复杂度为 。
代码
#include <cstdio>
const int MAXN = 1000000;
int n;
long long a[MAXN], A, B, C;
long long s[MAXN + 1], f[MAXN + 1];
template <typename T> inline T sqr(const T &x) { return x * x; }
inline long long y(const int a) {
return f[a] + A * sqr(s[a]) - B * s[a];
}
inline long long x(const int a) {
return s[a];
}
inline long long g(const int i) {
return 2 * A * s[i];
}
inline double slope(const int a, const int b) {
return static_cast<double>(y(a) - y(b))
/ static_cast<double>(x(a) - x(b));
}
int main() {
int n;
scanf("%d", &n);
scanf("%lld %lld %lld", &A, &B, &C);
for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i - 1];
static long long q[MAXN + 1];
long long *l = q, *r = q;
*r = 0;
for (int i = 1; i <= n; i++) {
while (l < r && slope(*(l + 1), *l) > g(i)) l++;
int j = *l;
// int _j = -1;
// for (int j = 0; j < i; j++) {
// if (_j == -1 || f[j] + A * sqr(s[i] - s[j]) + B * (s[i] - s[j]) + C > f[_j] + A * sqr(s[i] - s[_j]) + B * (s[i] - s[_j]) + C) _j = j;
// }
// j = _j;
// printf("i = %d, j = %d\n", i, j);
f[i] = f[j] + A * sqr(s[i] - s[j]) + B * (s[i] - s[j]) + C;
// printf("i = %d, _j = %d\n", i, _j);
while (l < r && slope(*r, *(r - 1)) < slope(i, *r)) r--;
*++r = i;
}
printf("%lld\n", f[n]);
return 0;
}