APIO 2010题解
P3628 特别行动队
思路:
斜率优化dp,主要以推公式为主,设s为前缀和数组,可得
f
[
i
]
=
f
[
j
]
+
a
∗
s
[
i
]
∗
s
[
i
]
+
a
∗
s
[
j
]
∗
s
[
j
]
−
2
∗
a
∗
s
[
i
]
∗
s
[
j
]
+
b
∗
s
[
i
]
−
b
∗
s
[
j
]
+
c
f[i]=f[j]+a∗s[i]*s[i]+a*s[j]*s[j]-2∗a∗s[i]∗s[j]+b∗s[i]−b∗s[j]+c
f[i]=f[j]+a∗s[i]∗s[i]+a∗s[j]∗s[j]−2∗a∗s[i]∗s[j]+b∗s[i]−b∗s[j]+c
f
[
j
]
+
a
∗
s
[
j
]
∗
s
[
j
]
−
b
∗
s
[
j
]
=
2
∗
a
∗
s
[
i
]
∗
s
[
j
]
+
f
[
i
]
−
a
∗
s
[
i
]
∗
s
[
i
]
−
b
∗
s
[
i
]
−
c
f[j]+a*s[j]*s[j]-b*s[j]=2*a*s[i]*s[j]+f[i]-a*s[i]*s[i]-b*s[i]-c
f[j]+a∗s[j]∗s[j]−b∗s[j]=2∗a∗s[i]∗s[j]+f[i]−a∗s[i]∗s[i]−b∗s[i]−c
看成一次函数解析式,可得纵坐标为
f
[
j
]
+
a
∗
s
[
j
]
∗
s
[
j
]
−
b
∗
s
[
j
]
f[j]+a*s[j]*s[j]-b*s[j]
f[j]+a∗s[j]∗s[j]−b∗s[j],横坐标为
s
[
j
]
s[j]
s[j], 斜率
k
=
2
∗
a
∗
s
[
i
]
k=2*a*s[i]
k=2∗a∗s[i],要使f[i]最大,也就是使截距最大,先画个图:
由于题目规定a<0,所以斜率肯定小于0,那么要截距最大我们就需要用单调队列维护一个上凸包,如上图所示
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
LL f[N], s[N];
int n, a, b, c;
int q[N];
double X(int i) //横坐标
{
return s[i];
}
double Y(int i) //纵坐标
{
return f[i] + a * s[i] * s[i] - b * s[i];
}
double slope(int i, int j) //斜率
{
return -(1.0 * (Y(i) - Y(j))) / (X(i) - X(j));
}
int main()
{
scanf("%d%d%d%d", &n, &a, &b, &c);
for (int i = 1; i <= n; i ++ )
{
scanf("%lld", &s[i]);
s[i] += s[i - 1];
}
int hh = 0, tt = 0;
q[tt] = 0;
for (int i = 1; i <= n; i ++ ) //维护上凸包
{
while (hh < tt && slope(q[hh], q[hh + 1]) <= -2 * a * s[i]) hh ++ ;
int j = q[hh];
f[i] = f[j] + a * (s[i] - s[j]) * (s[i] - s[j]) + b * (s[i] - s[j]) + c;
while (hh < tt && slope(q[tt], q[tt - 1]) >= slope(i, q[tt - 1])) tt -- ;
q[++ tt] = i;
}
printf("%lld\n", f[n]);
return 0;
}