当前位置: 首页 > news >正文

【题解】P2501 [HAOI2006] 数字序列 [思维 + dp]

P2501 [HAOI2006] 数字序列 - 洛谷

1.解题前思考

注意到需要先改变的点最少,其次才是代价最小

既然要改变,肯定是有一些 a_i 是固定不变的,作为严格递增的范本。

考虑 a_i 和 a_j(i\leq j),如果这两个点都是固定点,那应该满足 a_j-a_i\geq j-i

至此,我们确定做法:

(1)筛出固定点

(2)把两个固定点之间不合法段处理成合法段

2.深度思考

满足 a_j-a_i\geq j-i 的 a_i 和 a_j(i\leq j) 是一对固定点,我们尽量让固定点更多。

即满足以上条件且长度最长的 a 中序列,是最优方案。

注意到合法段是严格上升(严格阶梯状),不好计算代价,考虑定义 b[i]=a[i]-i

a_j-a_i\geq j-i \Rightarrow (a_j -j)-(a_i -i)\geq 0\Rightarrow b_j-b_i\geq 0

即 b 中的最长不降序列是最优方案。

这样 b 中的合法段是不降(一段段平的拼接)的,代价计算方便(看到后面就知道方便在哪)。

考虑将两个固定点之间的不合法段改成合法所需的代价

这个段内肯定没有在 b[i] 和 b[j] 之间的值,不然最长不降序列还能再长。

所以对于一个点来说,最终改成的值不是 b[i] 就是 b[j],这样代价才最小。

但我们又要求最终合法是不降序列,所以肯定是前一段 b[i],后一段 b[j]

反证:

如果前一段中靠近后一段的一段,改成 x(b[i]<x<b[j]) 代价比改成 b[i] 更小,

那么可以证明,这一段改成 b[j] 代价会更小。

如果后一段中靠近前一段的一段,改成 x(b[i]<x<b[j]) 代价比改成 b[j] 更小,

那么可以证明,这一段改成 b[i] 代价会更小。

3.构造代码

 b 中的最长不降序列,可以用二分 / 树状数组 / 线段树 O(Nlog\ N) 的时间求出来。

我这里选择了二分,同时增加一个合法点 n + 1,照顾到那些结尾不在 n 的最长不降序列。

二分结束后添加一个合法虚拟起点 0,照顾到那些开头不在 1 的最长不降序列。

然后一个个枚举不合法区间,由于最长不降序列可能有多个

这里选择每个点都求出最长不降序列大小 L[i],然后把每个点放到对应的 L[i] 桶中。

每次取区间时就从 L[j] -1 的桶中取 b[i] 小于等与当前 b[j] 的点当作 i\ (i<j)

再求出 sumi[x] 和 sumj[x] 两个数组,代表 i 到 x 全部改成 b[i]\ or\ b[j] 的代价。

枚举前一段后一段的临界点 k,求出最小值。

这个最小值只能代表这段不合法段的最小代价,不能代表这道题的答案。

考虑定义 dp 数组,dp[i] 代表从虚拟起点 0 到合法点 i 的最小总代价。

最后输出 dp[n + 1],详细见代码。 

时间复杂度最坏 O(N^3),但题目说 “随机生成”,可以看作 O(N^2),玄学可过。

注释代码:

#include<bits/stdc++.h>
using namespace std;typedef long long LL;
const int N = 35e3 + 10;int a[N], b[N];
int mn_end[N], len;   // [i]:长度为 i 的最长不降子序列(LIS)的最小结尾
int L[N];            // [i]:以 i 点为结尾 LIS 的长度 
vector<int> G[N];    // LIS 长度桶 
LL sumi[N], sumj[N];   // [k]:i 到 k 全部改成 b[i] / b[j] 的代价 
LL dp[N];             // [i]:从虚拟起点到合法点 i 的最小总代价 int main () {ios::sync_with_stdio(false);cin.tie(0);int n;cin >> n;for (int i = 1; i <= n; i ++) {cin >> a[i];b[i] = a[i] - i;}b[n + 1] = 1e9;   // 不一定所有最长不降子序列(LIS)的结尾都是 n// 为了所有非法段都能被处理到,加入一个所有 LIS 的结尾点 memset(mn_end, 0, sizeof(mn_end));int len = 0;     // mn_end 数组的当前长度 for (int i = 1; i <= n + 1; i ++) {int l = 0, r = len, p = 0;while (l <= r) {int mid = (l + r) >> 1;if (mn_end[mid] <= b[i]) {p = mid;l = mid + 1;}else {r = mid - 1;}}if (p == len) {len ++;      // 更新全局 LIS 长度 } mn_end[p + 1] = b[i];   // 长度为 p + 1 的 LIS 结尾最小值更新 // 如果 p + 2 也因为 i 可以被更新的更小,导致更新错误怎么办?// 答:不用担心// 假设后面有一个点 x,me[p + 1](new) < x < me[p + 2](new)// 并且 me[p + 2](old) < x < me[p + 3](old)// 这样会导致本应更新 p + 2 的 x 更新了 p + 3// 但很明显,不可能做到同时 < me[p + 2](new) 且 > me[p + 2](old)// 所以不用担心会更新错误 L[i] = p + 1;G[L[i]].push_back(i); }cout << n - len + 1 << "\n";  // 不合法点的数量,因为多算了一个 n + 1 memset(dp, 0x7f, sizeof(dp));    // 初始化最大值 G[0].push_back(0);          // 添加虚拟起点 b[0] = -1e9; dp[0] = 0;    // 保证所有点都能接它后面     for (int j = 1; j <= n + 1; j ++) {for (int i : G[L[j] - 1]) if (i < j && b[i] <= b[j]){sumi[i] = 0;for (int k = i + 1; k < j; k ++) {sumi[k] = sumi[k - 1] + abs(b[k] - b[i]);}sumj[j] = 0;for (int k = j - 1; k > i; k --) {sumj[k] = sumj[k + 1] + abs(b[j] - b[k]);}for (int k = i; k < j; k ++) {dp[j] = min(dp[j], dp[i] + sumi[k] + sumj[k + 1]);} }}cout << dp[n + 1] << "\n";return 0;
}

http://www.dtcms.com/a/515877.html

相关文章:

  • Xshell效率实战系列五:大文件传输封神技——断点续传+压缩传输双buff拉满
  • 【CTF】强网杯2025 Web题目writeup
  • 广东深圳网站建设wordpress局部内容
  • UCLAMP1211T.TCT TVS二极管瞬态电压抑制器Semtech升特 电源芯片芯片解析
  • GLM 智能助力・Trae 跨端个人任务清单
  • top命令的详解
  • 建行网站会员徐州市政工程招标信息
  • 开关电源的瞬态电流如何测试?
  • 性能测试 | 认识性能测试工具JMeter断言、关联、属性以及录制脚本的使用
  • 自编音乐结构理论
  • OpenAI发布AI浏览器Atlas:探索下一代网页交互新可能
  • DDD(二)对比 MVC 架构,DDD 的优劣势
  • 昆山网站建设 熊掌号怎么查名字有没有被注册商标
  • 网站页面好了怎么做后端利川市网站建设
  • [Agent可视化] [特殊字符]可观测体系 | 指标监控 | 分布式追踪 | eg慢请求诊断
  • C语言题目与练习解析:配套《柔性数组和动态内存易错点》
  • 在 IntelliJ IDEA 中启动多个不同端口的 Spring Boot 应用
  • 实战分享:一键自动化下载指定版本的Chrome及Chromedriver(附Python源码)
  • Jetson docker环境搭建
  • FVM (Flutter Version Manager)
  • 湖南手机响应式网站建设企业公司设计网站多少钱
  • 网站 为何要 备案嘉兴网站seo公司
  • stm32_小乌龟使用手册
  • Macs Fan Control Pro for Mac 电脑风扇控制软件
  • 广东哪家网站建设后台管理便捷wordpress配置文件数据库连接
  • 网站建设公司的公司哪家好xml是用来做网站的嘛
  • 17_AI智能体开发架构搭建之Flask集成swagger在线文档实践
  • 数据管理与数据库1.1-1.2
  • 完备的常州网站优化软件开发专业适合女生吗
  • Windows MCP.Net:解锁AI助手的Windows桌面自动化潜能