题解 P7447

· · 题解

前言

好毒瘤啊好毒瘤啊好毒瘤啊好毒瘤啊

upd on 3.23:哈哈,原来最后一部分是 \log n 分块,我还以为就是小常数暴力卡空间,学到了!

upd on 3.31:一处复杂度分析错误,感谢水军带你飞的指出。

暴力

考虑暴力线段树。

时间复杂度 O(m\min(\log_2n,a_i)),由于数据水可以拿很多分。

思路

考虑按照值域倍增分块,进制为 b

也就是说,[b^i,b^{i+1}) 会被分在一个块里,于是我们得到了 \log_ba_i 块。

我们把每个数再额外求出一个 s_i 表示它距离这一块最小值的距离。

对于每一块建立线段树。

修改时,我们在每一棵线段树上暴力,仍然在以下几种情况返回:

然后查询的时候就直接推标记返回真实值就好了。

这样的复杂度是多少的呢?

注意到我们在一棵线段树上修改的时候,如果 s_i 的最小值都 \geq x,复杂度是 \log_2n 的,因此总复杂度是 O(m\log_ba_i\log_2n) 的。

但是可能会有 s_i<x 需要被重构的节点把单次修改的复杂度搞的很大,这部分可以均摊分析一下。每个 s_i<x 必定会至少向下跌落 1 块,因此一个 a_i 最多只能向下跌落 \log_ba_i 块,一次跌落只会占用 \log_2n 的复杂度,均摊下来是 O(n\log_ba_i\log_2n) 的。

还有一个问题是 a_i\leq x 的部分,即和 x 同块的线段树上的复杂度。这部分事实上就等同于只分 1 块的暴力,由于每个数最多只能被减 b\log_ba_i 次,时间复杂度为 O(mb\log_2n\log_ba_i))

因此这题的时间复杂度是 O((n+m)\log_ba_i\log_2n+mb\log_2n\log_ba_i),空间复杂度是 O(n\log_ba_i)

不难发现 b 取一个接近 e 的数(比如 2 或者 4)就是最优的,但是被毒瘤出题人卡了空间。

于是我们可以将整个序列按 \log_ba_i 大小分块,块间线段树,块内暴力,这样线段树每次查询和修改只会暴力修改 O(1) 个块,在时间复杂度并没有提升的情况下空间复杂度直接变成了 O(n)

总结

一道很好的倍增练手题(迫真)。