《<完美的队列>命题报告》 - 学习笔记

首先不能看错题。每次的答案是所有队列的并的不同元素个数。

可以想到对于每个操作,求出它塞进去的元素消失的最晚时间,然后就可以把相同元素的操作放在一起。于是现在权值已经不重要了,我们只关心它这次塞进去的元素什么时候消失。

然后有整体二分或莫队的思路,或多或少都需要把序列分成若干块来保证复杂度,但是不确定能不能优化到 \(O(n\sqrt n)\) ,所以就不看了。

直接对序列分块。考虑求出一个操作在每个位置的消失时间。

先考虑它在一个零散位置什么时候消失。一个经典的想法是把“区间插入”差分,然后从左到右推每个位置的插入序列。那么在这个位置的消失时间就可以通过线段树二分在 \(O(\log n)\) 的时间内求出。共有 \(O(B)\) 个零散位置,所以复杂度 \(O(nB\log n)\)

然后是整块。对于每个整块,把操作序列拎出来跑 two pointers ,用线段树维护每个位置剩的空间,即可得到每个覆盖该块的操作的存活时间。复杂度 \(O({1\over B}n^2\log n)\)

于是获得了 \(O(n\sqrt {n}\log n)\) 垃圾做法。

优化整块

注意到一个操作只会有两个零散块的贡献,所以把所有整块的零散操作个数加起来仅仅是 \(O(n)\) 的。

所以对一个整块做 two pointers 时只需要暴力做零散操作的区间加减,用一个 tag 维护整块加减即可。总复杂度 \(O(n^2/B+nB)\) ,分别是所有操作覆盖的整块数和暴力给每个操作的零散块做区间加减的复杂度。

这时候已经可以冲过所有数据了。

优化零散块

仍然考虑一次做一整个块。在这一整个块里,某一些时间是确定会有一个插入操作的(即覆盖了这个块的操作),而剩下的时间就只有一个区间有插入操作。

处理出每个时间前缀的确定操作的个数(就是一个前缀和)和第 \(i\) 个确定操作的时间,然后对于每个位置,把零散操作拿出来跑 two pointers 即可。

总复杂度 \(O(n\sqrt n)\) ,非常牛逼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章