【Luogu P3628】【BZOJ1911】[APIO2010]特别行动队
题目:
链接:
大意:
把 \(n\) 个士兵分段成为若干个特别行动队,对于第 \(i\) 个士兵,他的战斗力是 \(x_i\),每个特别行动队的战斗力为 \(aX^2+bX+c\),其中 \(X\) 表示行动队了所有士兵的战斗力的和,求一个最好的分段方式使所有行动队的战斗力最大。
正文:
考虑用动态规划解决。设状态 \(f_{i}\) 表示前 \(i\) 个已经分好段的所有行动队的战斗力。那么动态转移方程是:
\[f_i=\max_{j=0}^{i-1}\{f_j+a(\sum_{k=j+1}^{i}x_k)^2+b(\sum_{k=j+1}^{i}x_k)+c\}
\]
可以通过前缀和 \(sum\) 简化式子(\(sum_i=\sum_{j=1}^{i}x_j\)):
\[\begin{aligned}f_i & =\max_{j=0}^{i-1}\{f_j+a(sum_i-sum_{j})^2+b(sum_i-sum_{j})+c\}\\ & = \max_{j=0}^{i-1}\{f_j+a\cdot {sum_i}^2-2a\cdot sum_i\cdot sum_j+a\cdot {sum_j}^2+b\cdot sum_i-b\cdot sum_j+c\} \end{aligned}
\]
直接枚举 \(i,j\) 得 \(\texttt{50}\) 分。
考虑用斜率优化。
那么选取 \(j,k\) 决策并且 \(0\leq j<k<i\),若 \(j\) 决策比 \(k\) 优, 即:
\[f_j+a\cdot {sum_i}^2-2a\cdot sum_i\cdot sum_j+a\cdot {sum_j}^2+b\cdot sum_i-b\cdot sum_j+c\geq f_k+a\cdot {sum_i}^2-2a\cdot sum_i\cdot sum_k+a\cdot {sum_k}^2+b\cdot sum_i-b\cdot sum_k+c
\]
通过移项可得斜率方程:
\[\frac{(f_j+a\cdot {sum_j}^2-b\cdot sum_j)-(f_k+a\cdot {sum_k}^2-b\cdot sum_k)}{sum_j-sum_k}\geq 2a\cdot sum_i
\]
代码:
const int N = 1000010;
int n, a, b, c;
ll f[N], sum[N], que[N], aa[N];
int head = 1, tail = 1;
double slope(int x, int y)
{
return (double) (f[x] - f[y] + a * sum[x] * sum[x] - a * sum[y] * sum[y] - b * sum[x] + b * sum[y] + 0.0)/(sum[x] - sum[y] + 0.0);
}
int main()
{
scanf ("%d%d%d%d", &n, &a, &b, &c);
for (int i = 1; i <= n; ++i)
{
scanf ("%lld", &aa[i]);
sum[i] += sum[i - 1] + aa[i];
}
for (int i = 1; i <= n; ++i)
{
while(head < tail && slope(que[head], que[head + 1]) >= 2.0 * a * sum[i]) head++;
f[i] = f[que[head]] + a * sum[i] * sum[i] - 2 * a * sum[i] * sum[que[head]] +
a * sum[que[head]] * sum[que[head]] + b * sum[i] - b * sum[que[head]] + c;
while(head < tail && slope(que[tail - 1], que[tail]) <= slope(que[tail], i)) tail--;
que[++tail] = i;
}
printf("%lld", f[n]);
return 0;
}