P4983 忘情 Sol
upd. 2023.3.14 更新了关于单调队列判定条件的一些表述。
首先考虑不限制 \(m\) 的怎么做。
式子可以化简为 \(\min\{\sum\limits_{[l_i,r_i]}[(\sum\limits_{j=l_i}^{r_i} a_j)+1]^2\}\)。
考虑 \(dp_i\) 表示当前选到截止 \(i\) 的一段,为 \([1,i]\) 的最小代价。
那么枚举上一个点 \(1 \le j < i\),则 \(dp_i=\min\{dp_j+\text{cost}(j+1,i)\}\)。
\(\text{cost}(j+1,i)\) 表示 \([j+1,i]\) 对于答案的贡献,即 \([(\sum\limits_{p=j+1}^ia_p)+1]^2\)。
令 \(s_i=\sum\limits_{j=1}^ia_j\),拆开式子。
\(dp_i=\min\{dp_j+(s_i-s_j)^2+2(s_i-s_j)+1\}\)。
进一步拆开得到
\(dp_i=\min\{dp_j+s_i^2+s_j^2-2s_is_j+2s_i-2s_j+1\}\)
看到同时含 \(i\) 和 \(j\) 项的乘积 \(s_is_j\),考虑斜率优化。
设 \(1 \le j_1 < j_2 < i \le n\),且 \(j_2\) 优于 \(j_1\)。
则有
\(dp_{j_1}+s_i^2+s_{j_1}^2-2s_is_{j_1}+2s_i-2s_{j_1}+1 \ge dp_{j_2}+s_i^2+s_{j_2}^2-2s_is_{j_2}+2s_i-2s_{j_2}+1\)
即
\(dp_{j_1}+s_{j_1}^2-2s_is_{j_1}-2s_{j_1} \ge dp_{j_2}+s_{j_2}^2-2s_is_{j_2}-2s_{j_2}\)
把含 \(i\) 的项提到左边,其余提到右边。
\(2s_i(s_{j_2}-s_{j_1}) \ge (dp_{j_2}+s_{j_2}^2-2s_{j_2})-(dp_{j_1}+s_{j_1}^2-2s_{j_1})\)
同时除以 \((s_{j_2}-s_{j_1})\),得
\(2s_i \ge \dfrac{(dp_{j_2}+s_{j_2}^2-2s_{j_2})-(dp_{j_1}+s_{j_1}^2-2s_{j_1})}{s_{j_2}-s_{j_1}}\)
这就是 \(j_2 > j_1\) 且优于 \(j_1\) 的条件。
发现右边一个与 \(s_{j_2}\) 有关,一个与 \(s_{j_1}\) 有关,可以看作一个函数,维护一个单调队列即可。
接下来,我们看到 \(m\) 限制,很自然地想到 wqs 二分。
设 \(f(i)\) 表示强制选 \(i\) 个区间的最小代价,发现 \(f(i)\) 单调递减且 \(f(i)-f(i+1)>f(i+1)-f(i+2)\)。
因为如果 \(f(i)-f(i+1) < f(i+1)-f(i+2)\),就有把 \(f(i+1) \rightarrow f(i+2)\) 的区间变化换到 \(f(i) \rightarrow f(i+1)\) 处才满足\(f(i)\) 为强制选 \(i\) 个区间的最小代价的定义。
我们确定了这玩意儿可以用 wqs 二分做,且这个图像呈下凸壳的左半部分(可画图找规律)。
也就是其斜率左侧小,右侧大。
我们二分到一个斜率,就可以进行一次 DP,来获得当前的最小代价(即截距)。
加上 \(m \times mid\) 就可以获得真实的代价。
(当然,对于和 \(m\) 不为同一斜率的点无法还原其答案,但是由于其答案最后肯定会被更新为与 \(m\) 同一条直线上的答案,所以没关系)
除此之外,由于当前要在答案最小的情况下,选择区间最少的方案。
那么考虑到当前答案从更早的地方转移过来一定更优,那么加强判定条件,改成 \(>\)。
没了。
#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
using namespace std;
const int N = 1e5 + 10;
int n, m, a[N], sum[N], q[N];
pair <int, int> dp[N];
inline int y(int j1, int j2) {
return (sum[j2] * sum[j2] - 2 * sum[j2] + dp[j2].fi) -
(sum[j1] * sum[j1] - 2 * sum[j1] + dp[j1].fi);
}
inline int x(int j1, int j2) {
return sum[j2] - sum[j1];
}
inline pair <int, int> check(int mid) {
int l = 1, r = 1;
for (int i = 1; i <= n; ++i) {
while (l < r && 2 * sum[i] * x(q[l], q[l + 1]) > y(q[l], q[l + 1])) ++l;
dp[i].fi = dp[q[l]].fi + (sum[i] - sum[q[l]] + 1) * (sum[i] - sum[q[l]] + 1) - mid, dp[i].se = dp[q[l]].se + 1;
while (l < r && y(q[r], i) * x(q[r - 1], q[r]) <= y(q[r - 1], q[r]) * x(q[r], i)) --r; q[++r] = i;
}
return dp[n];
}
signed main() {
ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> a[i], sum[i] = sum[i - 1] + a[i];
int l = -1e18, r = 0, res = 0;
while (l <= r) {
int mid = (l + r) >> 1; auto tmp = check(mid);
if (tmp.se <= m) res = tmp.fi + m * mid, l = mid + 1;
else r = mid - 1;
}
cout << res << endl;
return 0;
}