[APIO2010]特別行動隊
阿新 • • 發佈:2020-09-17
解析
轉移方程很容易推:\(f_i = \max(f_j + a * (s_i - s_j)^2 + b * (s_i - s_j) + c)\)
然後當 \(j>k\) 時,如果 \(j\) 更優
那麼 \(f_j + a * (s_i - s_j)^2 + b * (s_i - s_j) + c > f_k + a * (s_i - s_k)^2 + b * (s_i - s_k) + c\)
整理得:\((f_j + a * s_j^2 - b * s_j) - (f_k + a * s_k^2 - b * s_k) > 2 * a * s_i * (s_j-s_k)\)
因為 \(s_j-s_k\) 大於零
所以我們可以把不等式兩邊同除 \(s_j-s_k\) (不除 \(2*a\),當然也可以除,但注意 \(a < 0\),除過去要變號)
於是就成了 \(\frac{(f_j + a * s_j^2 - b * s_j) - (f_k + a * s_k^2 - b * s_k)}{s_j-s_k} > 2 * a * s_i\)
既然是大於號,那麼維護上凸殼
右邊單調減,單調佇列維護即可
\(Code\)
#include<cstdio> using namespace std; typedef long long LL; const int N = 1e6 + 5; int n , l , r; LL a , b , c , f[N] , q[N] , s[N]; double slope(int u , int v) { return 1.0 * ((f[u] + a * s[u] * s[u] - b * s[u]) - (f[v] + a * s[v] * s[v] - b * s[v])) / (s[u] - s[v]); } int main() { scanf("%d%lld%lld%lld" , &n , &a , &b , &c); for(register int i = 1; i <= n; i++) scanf("%lld" , &s[i]) , s[i] += s[i - 1]; q[l = r = 1] = 0; for(register int i = 1; i <= n; i++) { while (l < r && slope(q[l] , q[l + 1]) > 2.0 * a * s[i]) l++; f[i] = f[q[l]] + a * (s[i] - s[q[l]]) * (s[i] - s[q[l]]) + b * (s[i] - s[q[l]]) + c; while (r >= l && slope(q[r] , q[r - 1]) < slope(q[r] , i)) r--; q[++r] = i; } printf("%lld" , f[n]); }