一道很适合std::multiset的题目

CF2181D
链接:https://codeforces.com/problemset/problem/2181/D
题意:现在有个若干层的地图,一共n层。对于第i层,左右边界分别是\(x_{i,1}\)\(x_{i,2}\),其中含有k扇门,长度依次为\(l_1,l_2,...,l_k\)。左边界以左,右边界以右都视为墙,门也视为墙。现在请问,允许在不碰到墙的情况下在每一层中移动这些门但不允许重合或者交换次序,请问最多有多少个位置满足在每一层中都没有墙?
数据范围略去
思路:
\(\qquad\)首先,我们注意到,如果最后没被覆盖的部分是分开来的两段,那么一定可以移动一些门把这两段合起来,更多段的情况同理。所以只需要考虑只有一段没被覆盖的情况。如果最后存在一扇门,它的层的左右两边都是空的,那么我们可以移动它,使得它的两边至少有一边不是空的,所以我们只需要考虑只有一段没被覆盖且每一层的门都堆在左边或者右边的情况即可。这种情况下,每层的门都堆积在最左边或者最右边。
\(\qquad\)观察到,如果我规定最后的左边界必须小于等于某个值,每一层都尽可能地把门堆在左边,如果堆不下了就堆到右边,就是最优的。
\(\qquad\)容易想到枚举左边界,但是必须优化。我们首先找出每一层的所有可能的左边界(O(k)),这些情况看做一系列的“改变”,意思就是说将某一层的门的放置情况修改成某种情况,然后排序。我们让所有门初始位于某种状态,随后按照顺序将某层的左边界改变成新的左边界。我们需要动态维护未被在每一层上都未被任何门覆盖的区间。
\(\qquad\)左边界稍微简单一点,如果一次处理所有左边界相同的”改变“就可以解决问题,这样两边都是单调的。但是右边界比较麻烦。这相当于我们有一系列数字,每次操作使其中某个数字变大,每次操作后要求出这些数字的min。怎么办?
\(\qquad\)对于右边界我一开始的想法是,首先忽略左边界相关的一切。这题的数据范围太大,我们把门堆在右边形成的“更长的墙”的左端点离散化,然后每一次变化都看做线段树上的区间修改,查询线段树可以得知当前最左的被覆盖的位置,这个位置-1就是右边界了。但是,我是蒟蒻,我觉得这个太麻烦了。
\(\qquad\)于是我查看了标答,才意识到std::multiset很适合这道题目。
\(\qquad\)下次吸取教训。警示后人!

posted @ 2026-01-29 20:01  CN_SegmentTree  阅读(3)  评论(0)    收藏  举报