P5063 [Ynoi2014] 置身天上之森 题解
如果底下没有放正解复杂度可能还需要想很久。
但看见正解也要带一个 \(\log\) 就比较好想了。
题意
要求维护一颗线段树。
-
区间加。
-
区间求小于等于 \(x\) 的树,但也要包括被包含的非叶子节点。
思路
我们可以发现,对于非叶子节点的贡献,如果区间长度相同,我们可以一起统一处理。
考虑到一颗线段树上长度的数量很少,我们可以考虑分别维护每个长度的贡献。
现在,我们要想一个操作复杂度最多 \(\sqrt n \log n\) 的算法,可以维护上面两个操作。
考虑分块。
对每一个相同长度的节点的序列都进行分块。
然后就想普通分块一样,进行维护就可以了。
细节
首先,这道题最大难点可能是在你选出你要维护的区间时进行的二分操作。
这道题非常的毒瘤,给一个在弱的样例,数据给的特别强。
说几个比较容易错的细节。
-
在修改的时候,我们发现有一些节点可能不是它维护的整个长度都需要加。所以在二分出边界以后,还需要特判一下。
-
顺着上面的说,你在特判时,例如此时你最左边的节点只有一段需要加,此时你还要判断一下这个节点维护的最右边是否在你的最右边的节点的左边,可能稍微有一点绕。但你不特判这个,你可以获得四十分的高分。
-
针对一些空间上的优化,此题需要动态开空间,不可以直接开满,可以使用 \(vector\) 。
-
针对一些时间上的优化,例如你在特判时,你没有必要在此时修改完直接修改整块然后排序,因为如果你这样做,你每一次操作时需要修改的块可能会有四块。可以判断一下此时的零散块是否还需要修改,这样,就可以保证每次最多修改两个零散块。
-
还有一个常见的分块中二分的剪枝,最大最小值的特判,如果最小值大于询问,直接跳过,如果最大值小于等于询问,直接加上块长。
其他的就没什么了。
如果调试要数据,可以去看评论区。
这里再放一组数据。
样例。