特别行动队

易得 $\mathcal O(n^2)$ 状态转移方程 $f_i\gets \max\limits_{j=0}^{j<i}\{f_j+a(s_i-s_j)^2+b(s_i-s_j)+c\}$,特殊的,约定 $f_0=0$。

考虑化简式子,

$$
\begin{aligned}
f_j+a(s_i-s_j)^2+b(s_i-s_j)+c
&= f_j+a(s_i^2-2\cdot s_i\cdot s_j+s_j^2)+b\cdot s_i-b\cdot s_j+c \\
&= f_j+a\cdot s_i^2-2a\cdot s_i\cdot s_j+a\cdot s_j^2+b\cdot s_i-b\cdot s_j+c\
\end{aligned}
$$

设函数 $F(i)$ 由上式只含有 $i$ 的项组成,$G(j)$ 由上式只含有 $j$ 的项组成。

那么

$$
\begin{aligned}
F(i)
&= a\cdot s_i^2+b\cdot s_i\\
G(i)
&= f_j+a\cdot s_j^2-b\cdot s_j
\end{aligned}
$$

则上式即为 $F(i)+G(j)+c$,那么转移方程即为 $f_i\gets \max\limits_{j=0}^{j<i}\{F(i)+G(j)-2a\cdot s_i\cdot s_j+c\}$。

发现 $F(i)+c$ 是共有的,那么便是 $f_i\gets \max\limits_{j=0}^{j<i}\{G(j)-2a\cdot s_i\cdot s_j\}+F(i)+c$。

约定 $j_1<j_2$,那么当 $j_1$ 转移优于 $j_2$ 即 $G(j_1)+2a\cdot s_i\cdot s_{j_1}>G(j_2)+2a\cdot s_i\cdot s_{j_2}$ 时,则有$(f_{j_1}+a\cdot s_{j_1}^2-b\cdot s_{j_1}-2a\cdot s_i\cdot s_{j_1})-(f_{j_2}+a\cdot s_{j_2}^2-b\cdot s_{j_2}-2a\cdot s_i\cdot s_{j_2})>0$。

移项得 $s_i>\dfrac{(f_{j_1}+a\cdot s_{j_1}^2-b\cdot s_{j_1})-(f_{j_2}+a\cdot s_{j_2}^2-b\cdot s_{j_2})}{2a(s_{j_1}-s_{j_2})}$。

题目说了 $a<0,x_i>0$,也就是 $s_{j_1}-s_{j_2}<0$,所以不用变号。

那么 $j_1$ 到 $j_2$ 的连线我们是可以看做是斜率 $k=\dfrac{(f_{j_1}+a\cdot s_{j_1}^2-b\cdot s_{j_1})-(f_{j_2}+a\cdot s_{j_2}^2-b\cdot s_{j_2})}{2a(s_{j_1}-s_{j_2})}$ 的直线。

然后使用单调队列维护凸壳就可以了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++
char buf[1000000], *p1 = buf, *p2 = buf;
template <typename T>
void read(T &x)
{
    x = 0;
    int f = 1;
    char c = getchar();
    for (; c < '0' || c > '9'; c = getchar())
        if (c == '-')
            f = -f;
    for (; c >= '0' && c <= '9'; c = getchar())
        x = x * 10 + c - '0';
    x *= f;
}
template <typename T, typename... Args>
void read(T &x, Args &...y)
{
    read(x);
    read(y...);
}
int n;
ll a, b, c;
ll s[1000020];
ll f[1000020];
int q[1000020];
ll hd, tl;
ll FF(int x) { return f[x] + a * (s[x] * s[x]) - b * s[x]; }
ld slope(int j1, int j2) { return (1.0 * FF(j1) - 1.0 * FF(j2)) / (2.0 * a * (s[j1] - s[j2])); }
int main()
{
    read(n, a, b, c);
    for (int i = 1; i <= n; i++)
        read(s[i]), s[i] += s[i - 1];
    q[hd = tl = 1] = 0;
    for (int i = 1; i <= n; i++)
    {
        while (hd < tl && s[i] > slope(q[hd], q[hd + 1]))
            hd++;
        int j = q[hd];
        f[i] = f[j] + a * (s[i] - s[j]) * (s[i] - s[j]) + b * (s[i] - s[j]) + c;
        while (hd < tl && slope(q[tl - 1], q[tl]) > slope(q[tl], i))
            tl--;
        q[++tl] = i;
    }
    cout << f[n] << endl;
    return 0;
}
最后修改:2024 年 01 月 29 日
v我50吃疯狂星期四