【luogu P4331】Sequence 数字序列(保序回归)

Sequence 数字序列

题目链接:luogu P4331

题目大意

给你一个数组,然后你可以花费 1 的费用把一个位置的数加一或者减一。
然后要你用最小的费用使得数组变得严格递增。

思路

首先其实严格递增会有点不好搞,我们可以用这么一个方法把它变成不严格的。
就是把第 i 个数减去 i,然后做,最后的答案加上 i

然后考虑不严格的可以怎么搞。
然后就有一个算法叫做保序回归。

保序回归就是有一些偏序关系,然后连边会形成 DAG,然后一开始有值,你修改 xy 的费用是 |xy|pp 任意都可以)
然后方法其实就是整体二分,假设当前区间你二分的答案是 mid,你会发现如果一个位置要变成 xmid,那 mid 的答案一定会比 mid+1 的答案优,反之亦然。
那我们就可以求出哪些是 mid 哪些是 mid+1,如果是 DAG 你可以用网络流做,但是这里更简单是一个数组,那我们就可以用一个指针每个情况都试一下,选最优的情况继续就可以啦。

代码

#include<cstdio> #define ll long long using namespace std; const ll N = 1e6 + 100; const ll INF = 2147483647; ll n, a[N], b[N]; ll abs(ll x) {return x < 0 ? -x : x;} ll clac(ll i, ll x) {return abs(1ll * a[i] - x);} void slove(ll l, ll r, ll L, ll R) { if (l > r) return ; if (L >= R) return ; ll mid = (L + R) >> 1; ll val = 0; for (ll i = l; i <= r; i++) val += clac(i, mid + 1); ll minn = val, minp = l - 1; for (ll i = l; i <= r; i++) { val -= clac(i, mid + 1); val += clac(i, mid); if (val < minn) { minn = val; minp = i; } } for (ll i = l; i <= minp; i++) b[i] = mid; for (ll i = minp + 1; i <= r; i++) b[i] = mid + 1; slove(l, minp, L, mid); slove(minp + 1, r, mid + 1, R); } int main() { scanf("%lld", &n); for (ll i = 1; i <= n; i++) scanf("%lld", &a[i]), a[i] -= i; slove(1, n, -INF, INF); ll ans = 0; for (ll i = 1; i <= n; i++) ans += abs(a[i] - b[i]); printf("%lld\n", ans); for (ll i = 1; i <= n; i++) printf("%lld ", b[i] + i); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P4331.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示