BZOJ1911【APIO2010】特别行动队 <斜率优化>

Problem

【APIO2010】特别行动队

Time  Limit:  4  Sec\mathrm{Time\;Limit:\;4\;Sec}
Memory  Limit:  64  MB\mathrm{Memory\;Limit:\;64\;MB}

Description

![](https://www.lydsy.com/JudgeOnline/images/1911_1.jpg)

Input

![](https://www.lydsy.com/JudgeOnline/images/1911_2.jpg)

Output

![](https://www.lydsy.com/JudgeOnline/images/1911_3.jpg)

Sample Input

1
2
3
4
-1 10 -20
2 2 3 4

Sample Output

1
9

标签:斜率优化DP

Solution

首先易得DP\mathrm{DP}方程:设f[i]f[i]为考虑前ii人的最大收益,则f[i]=maxj=0i1{A×(SiSj)2+B×(SiSj)+C+f[j]}f[i]=\max_{j=0}^{i-1}\lbrace{A\times(S_i-S_j)^2+B\times(S_i-S_j)+C+f[j]}\rbrace
那么推一推:

f[i]=maxj=0i1{A×(SiSj)2+B×(SiSj)+C+f[j]}=maxj=0i1{A×Si2A×Sj2+2A×Si×Sj+B×SiB×Sj+C+f[j]}=maxj=0i1{2ASj×Si+(A×Sj2B×Sj+f[j])}+A×Si2+B×Si+C\begin{aligned} f[i]&=\max_{j=0}^{i-1}\lbrace{A\times(S_i-S_j)^2+B\times(S_i-S_j)+C+f[j]}\rbrace\\ &=\max_{j=0}^{i-1}\lbrace{A\times S_i^2-A\times S_j^2+2A\times S_i\times S_j+B\times S_i-B\times S_j+C+f[j]}\rbrace\\ &=\max_{j=0}^{i-1}\lbrace{-2AS_j\times S_i+(A\times S_j^2-B\times S_j+f[j])}\rbrace+A\times S_i^2+B\times S_i+C\\ \end{aligned}

发现中间2ASj×Si+(A×Sj2B×Sj+f[j])-2AS_j\times S_i+(A\times S_j^2-B\times S_j+f[j])是一次函数,那么对于两个位置p,q  (p<q)p,q\;(p<q),若qqpp优秀,则有:

Let  kp=2ASp,  bp=A×Sp2B×Sp+f[p]kpSi+bp<kqSi+bqSibpbqkqkp\begin{aligned} Let\;k_p=-2AS_p,\;b_p&=A\times S_p^2-B\times S_p+f[p]\\ k_pS_i+b_p&< k_qS_i+b_q\\ S_i&\le\frac{b_p-b_q}{k_q-k_p} \end{aligned}

按照此斜率维护单调栈即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <bits/stdc++.h>
#define MAX_N 1000000
using namespace std;
typedef long long lnt;
typedef double dnt;
template <class T> inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
int n, que[MAX_N+5], l, r; lnt A, B, C;
lnt s[MAX_N+5], k[MAX_N+5], b[MAX_N+5], f[MAX_N+5];
dnt calc(int x, int y) {return (dnt)(b[x]-b[y])/(dnt)(k[y]-k[x]);}
int main() {
read(n), read(A), read(B), read(C);
for (int i = 1, x; i <= n; i++)
read(x), s[i] = s[i-1]+x;
int l = 0, r = 0; que[0] = 0, b[0] = C;
for (int i = 1; i <= n; i++) {
while (l < r && calc(que[l], que[l+1]) <= s[i]) l++;
f[i] = k[que[l]]*s[i]+b[que[l]]+A*s[i]*s[i]+B*s[i];
k[i] = -2*A*s[i], b[i] = A*s[i]*s[i]-B*s[i]+C+f[i];
while (l < r && calc(que[r], i) <= calc(que[r-1], que[r])) r--;
que[++r] = i;
}
return printf("%lld\n", f[n]), 0;
}