NOI 2022 众数
1.前言
首先是:关于 \(\rm deque\) ,他死了但没有完全死。
然后是这个大样例说实话有点离谱,最初我在写 \(75\ \rm pts\) 部分分的时候,我动态开点线段树的 \(\rm insert\) ,没有处理好可能会有点被重复使用。我当时没意识到这个问题,就在操作四的时候人为对两个序列做了个启发式合并(不是线段树合并自带的启发式合并),再加上我当时魔改了线段树合并,就莫名其妙的把大样例过了,然后交上去 #11 这个点竟然也过了。离谱,太离谱了!
2.题解
首先我们先解决对正解比较有用的性质 \(C\) 。
对于绝对众数有一个比较简单的做法:摩尔投票。这个东西可以线性的求出一个序列的绝对众数,且支持合并。那么我们对每一个序列维护其摩尔投票的结果,然后询问时把所有序列的结果合并起来即可。
但是摩尔投票只能处理有绝对众数的序列,所以我们在修改元素值,合并序列的时要验证当前摩尔投票结果是否为绝对众数。验证的时候需要知道当前数一共出现了多少次,所以还需要一个权值线段树来维护每个数出现的次数。以上写起来是不难的,值得注意的是摩尔投票的合并,还是有一点小细节的。
我们再来考虑正解。
现在多了一个删除操作,我们发现这对两个东西有影响。一是数列的摩尔投票结果,二是操作四中合并序列的方法。
对于前者,我们在做动态开点线段树的时候充分利用非叶子节点,统计出来序列的众数出现次数,然后再线段树上二分找到众数是什么。
对于后者,我们要求合并是有顺序的,那么我们可以对序列做启发式合并。这里我们需要一个能访问序列开头元素,结尾元素的数据结构,比较好象的应该就是 \(\rm deque\) ,其他的还有 \(\rm list\) 等。
可惜的是,如果你在程序里面开了 \(1e6\) 个 \(\rm deque\) 的话,很遗憾,你会获得 \(0\) 分的好成绩。原因是你会 \(MLE\) ,\(\rm deque\) 会占用大量的内存(即使它是空的) 。 但事实上,\(\rm deque\) 确实可以,因为我们做的是启发式合并,所以只需要开一半的 \(\rm deque\) 即 \(5e5\) 个,这样就可以卡过去了。但是,安全起见,写 \(\rm list\) 一定比写 \(\rm deque\) 优,所以可以认为 \(\rm deque\) 已经死了。
复杂度:\(\mathcal O(C_m\log C_m)\)
代码:Link