P4983 忘情 Sol
upd. 2023.3.14 更新了关于单调队列判定条件的一些表述。
首先考虑不限制 的怎么做。
式子可以化简为 。
考虑 表示当前选到截止 的一段,为 的最小代价。
那么枚举上一个点 ,则 。
表示 对于答案的贡献,即 。
令 ,拆开式子。
。
进一步拆开得到
看到同时含 和 项的乘积 ,考虑斜率优化。
设 ,且 优于 。
则有
即
把含 的项提到左边,其余提到右边。
同时除以 ,得
这就是 且优于 的条件。
发现右边一个与 有关,一个与 有关,可以看作一个函数,维护一个单调队列即可。
接下来,我们看到 限制,很自然地想到 wqs 二分。
设 表示强制选 个区间的最小代价,发现 单调递减且 。
因为如果 ,就有把 的区间变化换到 处才满足 为强制选 个区间的最小代价的定义。
我们确定了这玩意儿可以用 wqs 二分做,且这个图像呈下凸壳的左半部分(可画图找规律)。
也就是其斜率左侧小,右侧大。
我们二分到一个斜率,就可以进行一次 DP,来获得当前的最小代价(即截距)。
加上 就可以获得真实的代价。
(当然,对于和 不为同一斜率的点无法还原其答案,但是由于其答案最后肯定会被更新为与 同一条直线上的答案,所以没关系)
除此之外,由于当前要在答案最小的情况下,选择区间最少的方案。
那么考虑到当前答案从更早的地方转移过来一定更优,那么加强判定条件,改成 。
没了。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现