1 条题解
-
0
「区间众数与中点」题解
问题分析
给定一个长度为 的序列 ,有 次询问,每次询问区间 内有多少个奇数长度的子区间 满足:
- 区间长度为奇数: 为奇数
- 区间中点 是区间的众数
关键限制:
- 区间长度为奇数,所以中点位置 是一个整数下标
- 要求 是整个区间的众数
- 众数定义:出现次数最多的数(可以有多个众数,此时要求 是其中之一)
核心观察
1. 中点的特殊性
对于奇数长度的区间 ,中点 将区间分成两部分:
- 左半部分:,长度
- 右半部分:,长度
区间总长度: 为奇数,所以 为偶数, 和 奇偶性相同。
2. 中点成为众数的条件
设 ,区间中 的出现次数为 。
要使 成为众数,需要满足:
- 的出现次数 任何其他数 的出现次数
- 即:对于所有 ,
在 中, 至少出现 1 次(在中点位置)。设左半部分 出现 次,右半部分出现 次,则 。
对于其他数 ,设左半部分出现 次,右半部分出现 次,则 。
条件变为:对于所有 ,
3. 简化条件
考虑所有数 中最多的出现次数,设 。
条件等价于:
或
$$L_x + R_x + 1 = max_y \text{ 且 } x \text{ 是众数之一(根据定义)} $$实际上,由于众数可以有多个, 是众数当且仅当:
4. 转化为前缀和
设前缀和数组 表示前 个位置中值 出现的次数。
则:
条件变为:
$$(pre[mid-1][x] - pre[l'-1][x]) + (pre[r'][x] - pre[mid][x]) + 1 \geq \max_{y \neq x} [(pre[mid-1][y] - pre[l'-1][y]) + (pre[r'][y] - pre[mid][y])] $$简化:
$$pre[r'][x] - pre[l'-1][x] + 1 \geq \max_{y \neq x} [pre[r'][y] - pre[l'-1][y]] $$即:
$$cnt_{[l',r']}(x) \geq \max_{y \neq x} cnt_{[l',r']}(y) $$这恰好就是 是区间 众数的定义,所以推导是自洽的。
问题转化
我们需要计数满足条件的 :
- 为奇数
- 是 的众数,其中
关键洞察
对于固定的中点 ,考虑所有以 为中点的奇数长度区间 :
- 区间长度:,
条件变为: 是区间 的众数。
算法思路
暴力方法(不可行)
对于每个询问 ,枚举所有可能的区间 ,检查条件。复杂度 或 ,不可行。
优化思路
由于 ,,需要接近 的算法。
观察1:对于固定中点
对于每个位置 ,我们可以预处理:最大的 使得 是 的众数。
但一个更重要的观察:如果 是 的众数,那么它也是 的众数对于所有 吗?不一定。
反例:考虑序列 [1,2,1],中点位置2(值2):
- :区间[2,2],2是众数
- :区间[1,3],2出现1次,1出现2次,2不是众数
所以性质不满足单调性。
观察2:众数出现的必要条件
设 ,区间 中 出现次数为 。
是众数需要满足: 对于所有 。
等价于:?不一定,众数不需要超过半数。
实际上,众数只需要出现次数最多,不一定过半数。
分块思想
由于 和 都很大,考虑使用莫队算法离线处理询问。
但是莫队算法处理的是区间查询,而我们需要计数子区间。可能需要二次离线莫队。
另一种思路:枚举中点
对于每个位置 作为中点,考虑它能向右扩展多少。
设 表示以 为中点,满足条件的最大区间半径 。
然后对于询问 ,我们需要计数所有 满足:
这可以转化为二维数点问题。
计算
对于每个中点 ,我们需要找到最大的 使得 是 的众数。
由于众数判断需要知道所有数的出现次数,直接计算 是困难的。
正解思路
关键洞察:出现次数差
定义 $diff(i,k) = cnt_{[i-k,i+k]}(a[i]) - \max_{y \neq a[i]} cnt_{[i-k,i+k]}(y)$
条件 是众数等价于 。
考虑随着 增加, 的变化:
- 当 增加1,区间扩展一个位置到左边 和右边
- 可能增加 0, 1 或 2
- 其他数的计数也可能增加
不是单调的,但我们可以对每个 二分查找最大的 使得 。
高效计算
我们需要快速查询区间 中:
- 的出现次数
- 其他数的最大出现次数
可以使用可持久化线段树维护每个前缀的权值出现次数。
对于区间 , 的出现次数 =
其他数的最大出现次数 = 区间众数的出现次数(如果众数不是 )或次大出现次数(如果众数是 )。
这需要查询区间众数,可以使用摩尔投票法+线段树,但需要维护每个区间的候选众数和计数。
实际可行算法
由于 ,我们可以对每个值 单独处理。
对每个值 的处理
考虑所有 的位置 。对于每个这样的 ,我们想知道以 为中点的区间 中, 是否能成为众数。
在区间 中, 出现次数 = 该区间内 的位置数。
其他数的最大出现次数 ≤ 区间长度的一半?不一定。
实际上,我们可以对每个 ,找到最大的 使得:
$$cnt_v([i-k,i+k]) > cnt_y([i-k,i+k]) \ \forall y \neq v $$或者
$$cnt_v([i-k,i+k]) \geq cnt_y([i-k,i+k]) \ \forall y \neq v $$且 是众数之一。
转换为连续段问题
对于固定的 ,考虑所有 的位置。对于每个这样的 ,向左右扩展,直到遇到某个位置使得其他数的出现次数超过 。
设 表示向左最多能扩展到什么位置, 表示向右最多能扩展到什么位置,使得 是以 为中点的区间的众数。
最终算法框架
预处理阶段
- 对序列分块,块大小 或
- 预处理每个块内每个值的出现次数前缀和
- 对于每个位置 ,预处理以 为中点的最大半径 ,使得 是众数
- 使用双指针扩展,利用分块信息快速计算区间内各值出现次数
- 复杂度 或
查询阶段
对于询问 :
- 计算所有完全在 内的区间 满足
- 这可以转化为:对于每个 ,满足条件的 数量为
- 因为 需要满足 且
所以答案 =
但是这样会重复计算 的情况(长度为1的区间),题目中长度为1的区间自然满足条件(唯一的元素是众数)。
优化查询
我们需要快速计算
将区间分成三部分:
- 左半部分: 靠近 ,限制是
- 中间部分: 不受边界限制,限制是
- 右半部分: 靠近 ,限制是
具体地:
- 对于 ,限制是
- 对于 ,限制是
- 对于中间的 ,限制是
但是 不是常数,所以需要更精细的处理。
实际实现策略
由于 很大,需要 或 的查询。
可以考虑预处理前缀和数组:
- (等差数列和)
然后对于询问 ,可以 计算:
通过分类讨论 的位置。
复杂度分析
预处理
- 计算 : 或
- 预处理前缀和:
查询
- 每个询问 或
- 总复杂度: 或
总结
本题是一个复杂的数据结构+组合计数问题:
- 将问题转化为对每个中点求最大扩展半径
- 使用分块+双指针预处理
- 将询问转化为前缀和计算
- 通过分类讨论实现快速查询
- 1
信息
- ID
- 5903
- 时间
- 3000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者