【Luogu P3628】【BZOJ1911】[APIO2010]特别行动队

题目:

链接:

洛谷链接

BZOJ链接

大意:

\(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;
}


posted @ 2020-03-15 03:21  Jayun  阅读(184)  评论(0编辑  收藏  举报