P4694 Raper 题解 = CF802O
算法:线段树维护,模拟费用流。
你需要生产
你知道每天 A、B 工厂分别加工一张光盘的花费。你现在有
求生产出
建出费用流模型:把每个光盘视作在每一天之间流动。把每一天抽象出一个点
注意这题还有要求容量限制,所以
直接费用流必挂。考虑模拟费用流。
我们发现:如果把流了一条 (
;流了一条 )
。一次增广路必然是在两个地方插入了一个 (
和一个 )
。
而且最终的括号序列一定顺次形成一个合法括号序列。
既然涉及到括号序列,就考虑 (
看作 )
看作
增广路一共分两种:一种是不回退边的,即 ...(...)...
;另一种就是 ...)...(...
。
不回退边的显然没有任何限制(因为 )
和 (
之间的 .
都是能回退的,也就是之前有用过的。
能回退是什么意思?考虑前缀和的话,就是这一段的
所以我们现在的目标是:分别找到这两类增广路的
不妨记增广路增广路
应用增广路:① 会使
尝试用线段树维护
问题变成如何维护一颗线段树支持这些东西。考虑每个结点要维护什么信息。
记这个结点对应的区间是
一个很显然的东西是要维护两个值
这里有一个很巧妙的转化:因为我们每次都只询问整个区间(根结点)的信息,而
于是我们在结点上维护一个
不要忘了我们的初心是找增广路。所以在结点上还要维护两个 pair:
根据直觉,还要再维护一个
但这样还是无法更新,缺少信息。(然后就发挥想象力了)
维护一个
维护一个
综上,我们在一个结点上维护了
当然,上面提到的所有都是在 val 上存的值。结点还有 tag,但是 tag 很简单,tag 只涉及对
接下来就考虑 val 和 tag 的运算。
-
tag * tag。简单,相加即可。
-
val * tag。难度稍有上升。考虑使一个区间的
加减会导致什么,显然 会加减对应的数。但其他的 等等,都是一个相对于 的定义,而加减是整体的加减,所以除了 都不会变。 -
val + val,即 pushup。线段树维护最难的部分。
考虑如何用两个子结点推出本身的信息。记左儿子为
,右儿子为 。-
,这个很简单,就是两个子结点的 取较小。就这个简单了 -
,也很简单。比较一下两个子结点哪个 的 更小,对应更新即可。 同理。 -
,这个是没有限制的。整个结点的 有三种可能:等于左儿子的 ,或等于右儿子的 ,或由左右儿子共同组合。我们在这三种情况中取最优即可。前两种都很简单,是
和 ;看左右儿子共同组合的情况。这种情况显然只有一个最优: 。 -
,这个同样是没有限制的。和 类似,不过左右儿子组合的最优是 。 -
更新
。这些放在一起,是因为都需要根据 和 的大小分类讨论。但是在分类讨论之前,有一点比较显然:可以用
和 尝试更新本身的 。开始分讨。时刻清楚意识到我们在分讨内部要做的事:更新
,更新 。-
当
。先考虑非左右儿子组合的。
因为
,所以 的 无论怎么选,在 中总有更小的 ,因此我们可以用 更新本身的 。再考虑左右儿子共同组合的可能性。
即我们要在
中选出一个 ,在 中选出一个 。取 , 要满足 。这个式子可以拆成:
和 。上面说过,
的部分一定大于整体的 ,所以前者的限制略去。我们对 没有限制了,显然我们会选择 。又
,所以后者的限制改写为 。在这个限制下使 最小,显然我们应该取 。注意 表示的是可行前缀中最优的。好,截至这里。我们在这种情况下只剩更新
和 了。考虑
,时刻铭记 表示的是可行的前缀中最优的。因为 的任何一个 都大于整体的 ,所以 如果最终的在 中选,一定会选 ;但因为 可以全部跨过去,所以还有一种可能是 。 就没有选择了,只能选 ,没办法跨越全部。至此,我们完成了一种情况的分讨,还剩两种。 -
当
。与上一种相似,太好了,我们可以跳过。 -
当
。此时最小值在两边都能取到,意味着任何 不能完全覆盖一边。因此更新 只能用 和 (左边的后缀和右边的前缀拼接)。 , 。
-
-
建立这么一颗线段树,每次查询对应的
终于做完啦!—— 吗?
直接这么写就错了!!!
这里有一个坑,当修改了
还有一个在代码实现细节上的小技巧:当我们更新
可以额外设置
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!