[BZOJ1911][Apio2010]特别行动队
试题描述
输入
输出
输入示例
4 -1 10 -20 2 2 3 4
输出示例
9
数据规模及约定
题解
DP,令 f(i) 表示前 i 个士兵可以达到的最大修正后的战斗力,则有 f(i) = max{ f(j) + d(j+1 ~ i) | 0 < j < i },其中d(i ~ j)表示第 i 个士兵到第 j 个士兵组成一队修正之后的战斗力。展开之后得到
f(i) - a·Si2 - b·Si - c = f(j) + a·Sj2 - b·Sj - 2·a·Si · Sj
发现蓝色部分只与i相关,黄色部分只与j相关,绿色部分随 i 的增加是单调递减的,橙色部分只与j相关,把它们依次换元成b, y, k, x,得到
b = y - k · x,变形得 y = kx + b,斜率k单调递增,我们要求的是b的最大值,所以维护所有(x, y)点构成的上凸壳即可。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *tail; inline char Getchar() { if(Head == tail) { int l = fread(buffer, 1, BufferSize, stdin); tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 1000010 #define LL long long int n, a, b, c, S[maxn], q[maxn], l, r; LL f[maxn]; double slop(int i, int j) { double yi = i ? f[i] + (double)a * S[i] * S[i] - (double)b * S[i] : 0.0, yj = f[j] + (double)a * S[j] * S[j] - (double)b * S[j]; double xi = i ? S[i] : 0, xj = S[j]; return (yi - yj) / (xi - xj); } int main() { n = read(); a = read(); b = read(); c = read(); for(int i = 1; i <= n; i++) S[i] = S[i-1] + read(); f[0] = 0; l = r = 1; q[1] = 0; for(int i = 1; i <= n; i++) { while(l < r && slop(q[l], q[l+1]) > 2.0 * a * S[i]) l++; int j = q[l]; f[i] = f[j] + (LL)a * S[j] * S[j] - (LL)b * S[j] - 2ll * a * S[i] * S[j] + (LL)a * S[i] * S[i] + (LL)b * S[i] + c; while(l < r && slop(q[r], i) >= slop(q[r-1], q[r])) r--; q[++r] = i; } printf("%lld ", f[n]); return 0; }