CF1693D Decinc Dividing——值域有连续性的 dp 通用分治策略
这个分治策略其实跟整体二分差不多,但是它的应用面比较单一和具有针对性。
通常是 只有 段。然后我们通过分治来看 的应该是哪一段。
复制def solve(l,r) if dp(l)==dp(r) fill dp(l+1),dp(l+2),...,dp(r-1) with dp(l) return mid := l+r>>1 calc dp(mid) solve(l,mid) solve(mid,r)
有时,这个 比较小,从而分治的复杂度就是 的,其中 为暴力计算一处 值的复杂度,比如 USACO23Feb Watching Cowflix。
有时,这个 比较大,比如 CF1693D Decinc Dividing,但是可以 计算一个连续段中的某处的 值,从而同一层的总复杂度是 的,总共就是 。
还有时,它满足其他优秀的性质,从而使得复杂度仍然是 的。
当你发现计算一处 dp 值非常容易,却苦于对于每一处都计算 dp 值的低效时,你可以尝试这种分治策略。
当你发现 dp 值具有单调性时,你也可以往这方面想想,因为如果这些连续段的 dp 值不单调也很难满足一些很好的性质。(你看,上面两道题都是单调的。)
暴力判断一个长为 的序列是否可以被划分为 IS 和 DS 的并就是一个 NOIP 模拟赛的套路。设 表示到了 处将 选进 DS,IS 的最小末尾值,以及选进 IS,DS 最大末尾值 ,即可简单转移。复杂度 。
运用上文的分治做法即可得到 复杂度,原因就是上面的第二个“有时”。
法2:
【套路1】判断一个数列 是否能够划分成 一个上升子序列 和 一个下降子序列 的并,只需判断是否存在 个元素 ,使得 或 。
【套路2】如何统计包含【套路1】中四元组的子段的数量?经典数形状问题。先把 的二元组找到,把 对应的最大的 记作 ,然后枚举 ,判断所有 且 已被 activate 的 数量,然后用 作为 去 activate 所有没有被 activate 的 ,然后将其从 set 中删除。
法3:
【套路3】对于一处 , 均只有 种可能的取值。
利用这个性质,用记忆化来优化暴力的枚举左端点再往右计算 dp 值的 做法即可做到 。
具体来说,枚举一个左端点 ,设 计算一次暴力 dp 的 dp 数组为 ,则对于一处 ,如果发现 且 ,则更大的 也是跟上一轮一样的,不用继续了,直接沿用上一轮的结论即可。否则,从 的视角考虑 的 的个数,很明显只会有 种(因为不可能来回地变化,这个关于 也是单调的啊)。于是就线性了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】