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

[JOI Open 2016] 摩天大楼

JOI Open 2016 摩天大楼

标签插入/连续段 DP

\(S = | f_1 - f_2| + | f_2 - f_3| + \cdots + | f_{N-1} - f_N|\)

因为 \(S\) 内的元素只跟相邻两项相关,考虑插入 DP。

考虑对于现在序列的两个相邻的数,以后有没有新的 \(a_i\) 插入是有巨大影响的(符号都变了),所以是需要维护若干个连续段的。

先将 \(a\) 从小到大排序,去掉绝对值。接着依次插入 \(a_i\)

\(f(i, j, l)\) 表示插入了前 \(i\) 个数,有 \(j\) 个连续段,当前的 \(S = l\) 的方案数。(\(S\) 为当前确定的项的总和,即连续段内部的差值之和)。然后插入 \(a_i\),结果傻眼了,因为我们不知道 \(a_i\) 左右两边的是啥,算出来 \(l'\)

这时我们可以用一个经典的小 trick,只动态维护 \(\le a_i\) 的折线之和。如下图,我们把 \(f\) 丢到一个平面上,则红色折线的总长度就是答案。

image

而我们只需要维护 \(\le a_i\) 的折现之和即可。(下图中蓝色的部分。)

image

而加入 \(a_{i + 1}\) 后,只需要加入下图中绿色的部分即可。

image

这绿色的部分每一段都的长度都是 \(a_{i + 1} - a_i\)。而有多少段绿色呢?不难发现相邻的连续段之间都有 \(2\) 段(上去一段,下来一段)。图中有 \(3\) 个连续段,绿段有 \(2 \times (3 - 1) = 4\) 段。

但是需要注意,可能当前最后一个连续段后面还有连续段(如下图),这时绿段又会多一个。同理,当前第一个连续段前面也可能还有连续段,也会影响绿段的数量。

image

所以我们的 DP 状态要拓展为 \(f(i, j, l, k = 0/1/2)\),最后一维表示最前面、最后面是否还有连续段(因为是一样的,记个数量即可。)

平时的连续段 DP,转移分三种:自己新建一段/插到某个段的开头/结尾/插到两个段中间,使得他们合并。

对于没有 \(k\) 不改变的情况,这三种都存在。而对于改变 \(k\) 的情况(就是 \(k + 1\) 的情况)只有两种

  • 自己新建一段在开头/结尾。(1)
  • 插入到开头那个段的开头/结尾那个段的结尾。(2)

总是就是这个新加入的数必须在整个序列的头/尾。否则如果后面都没有数插到头/尾,这种情况可能会被算重。(因为这和 \(k\) 不变是完全一样的。)

转移时 \(l' = l + (a_i - a_{i -1})(2j - k)\),使 \(k\) 增加时要乘一个 \(2 - k\) 的系数。(可能又两种选择)

初始状态:\(f(1, 1, 0, 0) = f(1, 1, 0, 2) = 1, f(1, 1, 0, 1) = 2\),目标状态:\(f(n, 1, l, 0)(l \le L)\)

时间复杂度:\(O(n^2L)\)

最后附上转移式子:

int v = dp[p][j][l][c]; dp[p][j][l][c] = 0; // c 是上文中的 k,从 i - 1 转移到 i(滚动了)
if (!v) continue;
int nl = l + (a[i] - a[i - 1]) * (2 * j - c);
if (nl > m) continue;
// Plus(u, v, k) 表示 u += v * k
Plus(dp[q][j][nl][c], v, 2 * j - c); // 插入到某段的开头/结尾
Plus(dp[q][j + 1][nl][c], v, j + 1 - c); // 自成一段
Plus(dp[q][j - 1][nl][c], v, j - 1); // 使合并两段
if (c < 2) {Plus(dp[q][j][nl][c + 1], v, 2 - c); // (2)Plus(dp[q][j + 1][nl][c + 1], v, 2 - c); // (1)
}

总结

运用了一个动态维护 \(\le a_i\) 折线长度的小 trick,还是很神奇的。

最初以为使 \(k + 1\) 的转移也是三种都可以,然后被狠狠地坑了一把。提醒我们要多造点极端小样例。

转移还是比较恶心的。

http://www.rkmt.cn/news/89008.html

相关文章:

  • 家乐事净水器加盟费多少?0加盟费+装修补贴+区域保护,全程扶持解读 - 资讯焦点
  • zz深入了解LlamaIndex实现Agent代码和原理
  • linux: gdb调试器
  • 6 个最佳开源 AI 仪表盘工具
  • ①【openFuyao】智算时代的异构算力连接器
  • 毕业设计实战:基于SSM+MySQL的图书商城管理系统设计与实现,从需求到测试全流程拆解,新手也能轻松通关!
  • #题解#洛谷P1120 小木棍#搜索#剪枝
  • 毕业设计实战:基于Java+MySQL的校园二手书交易平台设计与实现,从需求到上线全流程避坑指南!
  • 实时图形工具包GLG Toolkit:工业领域HMI数据可视化的优选产品
  • 使用Junit测试
  • 人类文明可通过技术手段(如加强航天器防护、改进电网设计)缓解地球两极反转带来的影响
  • 智能客服
  • ComfyUI由浅入深全方位,AI生图,AI生成视频,AI动画教程
  • 决策树模型实战指南:避免过拟合、欠拟合与无关特征
  • YashanDB数据库的读写分离策略分析
  • 2025年最后一个月,公司需要注意什么?
  • 可信数据空间:驱动社会高质量发展的“数字基石”,必要性无可替代
  • 自动驾驶汽车与利益相关者互动的功能安全与网络安全分析高效的方法
  • HarmonyOS 5 极致动效实验室:给 UI 注入“物理动效”
  • springboot基于vue的比亚迪新能源汽车销售系统的设计与实现_1061pdmq
  • springboot基于vue的拜泉县房屋拆迁安置信息管理系统设计与实现_yt2m39o4
  • springboot基于vue的故宫博物馆文创网店商城系统的设计与实现_oj61901i
  • 什么是UUID,怎么组成的?
  • YashanDB数据库的多活架构设计及实施要点.
  • Simple Form性能优化完整指南:5个实用技巧让Rails表单快如闪电
  • Vue Flow与Pinia状态管理实战指南:构建高效可视化应用
  • YashanDB数据库的多活架构设计与实施经验分享
  • 为什么你的滑动窗口总是写不对?
  • springboot基于vue的春节物资购买平台的设计与实现_88a5r046
  • AMD ROCm平台上的YOLOv8目标检测:从入门到精通的5步优化指南