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;
}
posted @ 2022-11-03 16:58  MistZero  阅读(30)  评论(0编辑  收藏  举报