众数杀手
感觉坑越开越多了。
最好的写法就是 set 找前驱后继。
rsrams
已收录于“lxl 根号数据结构”。
CF1446D1
给定序列 ,求最长的满足区间众数有至少两种的区间长度。
。
有一个结论:全局众数一定是答案的众数之一,否则你可以扩展区间使得全局众数的出现次数追平区间众数。
于是我们首先知道一个答案是全局众数,然后利用 ,枚举另一种颜色,将全局众数视为 ,这种颜色视为 ,其他颜色视为 ,则若另一种颜色被计入答案,则答案的一个必要条件是区间和为 。
我们直接用前缀和计算区间和为 的最长区间发现它对了,于是我们来证明不合法的区间一定不优:
- 区间和不为 且区间合法当且仅当区间众数的出现次数大于全局众数,于是我们可以知道它肯定比答案劣,因为可以扩展它使得全局众数追平。
CF1446D2
。
做法一:无法故技重施了,我们考虑根号分治。
出现次数大于根号的只有根号个,我们可以跑一遍上面的做法,解决。
出现次数小于根号的我们考虑如何 解决,
考虑摒弃掉原来的想法,直接 解决出现次数 的区间们。
枚举出现次数 ,从左往右跑双指针,右端点右移时如果让一个值超过了 那么左端点右移,合法了就统计答案。
做法二:考虑小块的另一种处理方式:枚举小块中第一个出现的元素 ,设全局众数为 ,则我们只需要考虑 前面 个 (不能超过上一个小块元素),和后面 个 (可以超过下一个小块元素),把这些有用的元素扔进一个新数组里跑。
处理一个小块元素的复杂度是 的,由于小块元素总个数不超过 ,复杂度 。
做法三:拒绝根号分治,设 的出现次数是 ,如果我们能用 的复杂度处理每个颜色,那我们就可以达到 。
考虑对该颜色求出所有“有用点”,具体来说从左到右枚举每个位置,每个位置匹配它后面的第一个 并将其删掉,再从右往左枚举,每个位置匹配它前面的第一个 并将其删掉,这就是所有可能出现在答案中的 ,使用 std:set。
而没出现在答案中的 起到了分隔符的作用,你不能让答案跨过这些 ,不会处理的话就暴力跑几个 扔进序列,这样前缀和就可以处理掉了。
一种简单的写法是直接对于每个元素找前面的一个 后面的一个 ,然后将它们删掉,但这是假的(如果是绝对众数 就可以前找一个后找一个),改成前面找一个后面找两个就对了。
总复杂度 。
做法四:考虑把做法三的 set 换成链表,你只需要支持对于每个元素查找他自己颜色的后继以及全局众数的后继即可,。
P8349 [SDOI/SXOI2022] 整数序列
给定颜色序列和对应数组 , 次询问两种颜色 ,试求出所有满足 出现次数相同的区间中 出现的位置的 的和最大值。
。
考虑根号分治,设 为块长。
首先有一个 解决 的暴力:将两种颜色的位置提取出来,归并一下,相当于要跑上面那个题的区间和为 的 和最小的区间,对于每个 前缀和记录前缀 和最小值即可。
那么我们小块对小块和大块对大块就直接解决了。
复杂度证明:小块对小块不用说,,大块对大块的话求出本质不同的询问们需要 ,拆开可得 。
那么考虑小块对大块:
考虑采用上面的做法,利用小块暴力匹配大块元素,扔进序列跑暴力,这里的匹配也可以采用链表,但需要维护每个元素前后的每个大块元素,较为麻烦,可以用 std::set 带 ,更好写一点,复杂度是 。
总复杂度是 ,取 可以平衡复杂度为 。