BZOJ 1911 [APIO2010] 特别行动队 - 斜率优化dp
题解
非常显然的$O(n^2)$ 的dp转移方程 :
$ F_i = \max( F_j + a \times (S_i - S_j) ^2 + b \times (S_i - S_j ) + c) $ 数组S为前缀和
进行一波分离后变成了 $ F_j + a \times (S_j)^2 - b \times S_j = 2a \times S_i \times S_j - b \times S_i - c + F_i$。
左边的一串式子为纵坐标, $S_j$ 为横坐标, $2a \times S_i $当做斜率, 套上斜率优化板子就可以了
注意维护的是上凸壳
代码
1 #include<cstring> 2 #include<algorithm> 3 #include<cstdio> 4 #define rd read() 5 #define rep(i,a,b) for(int i = (a); i <= (b); ++i) 6 #define per(i,a,b) for(int i = (a); i >= (b); --i) 7 #define ll long long 8 using namespace std; 9 10 const int N = 1e6 + 1e5; 11 12 ll f[N], a, b, c, s[N], q[N], sum[N], n; 13 14 int read() { 15 int X = 0, p = 1; char c = getchar(); 16 for(; c > '9' || c < '0'; c = getchar()) if( c == '-') p = -1; 17 for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0'; 18 return X * p; 19 } 20 21 double caly(int A) { 22 return f[A] + a * sum[A] * sum[A] - b * sum[A]; 23 } 24 25 double calk(int A, int B) { 26 double ax = sum[A], bx = sum[B], ay = caly(A), by = caly(B); 27 return (by - ay) / (bx - ax); 28 } 29 30 31 int main() 32 { 33 n = rd; 34 a = rd; b = rd; c = rd; 35 rep(i, 1, n) s[i] = rd, sum[i] = sum[i - 1] + s[i]; 36 int l = 1, r = 1; 37 rep(i, 1, n) { 38 while(l < r && calk(q[l], q[l + 1]) >= 2 * a * sum[i]) l++; 39 ll x = sum[i] - sum[q[l]]; 40 f[i] = f[q[l]] + a * x * x + b * x + c; 41 while(l < r && calk(q[r - 1], q[r]) <= calk(q[r], i)) r--; 42 q[++r] = i; 43 } 44 printf("%lld\n", f[n]); 45 } 46