《(蒲公英)众数查询中块数选择的优化:平衡预处理与查询时间》
《(蒲公英)众数查询中块数选择的优化:平衡预处理与查询时间》
在分块算法中,块大小(或块数)的选择核心是平衡预处理时间和查询时间,使得整体复杂度最优。对于本题(众数查询问题),将块数 t 设为 sqrt(Q * log2(n)),本质是通过数学推导让预处理和查询的时间复杂度尽可能接近,从而最小化总耗时。
关键:明确预处理与查询的时间表达式
要理解块数的选择,需先拆解两部分核心耗时:
1. 预处理时间
pre(i) 函数的作用是:对第 i 个块,从块的起始位置 L[i] 遍历到数组末尾,记录每个位置对应的 “当前众数”,存储在 mo 数组中。
-
单次
pre(i)的时间复杂度为O(n)(遍历从L[i]到n,共O(n)步)。 -
总共有
t个块,因此预处理总时间为O(t * n)。
2. 查询时间
每次查询 qes(l, r) 需要处理三部分:左边角块、右边角块、中间完整块。
-
中间完整块可直接用预处理的
mo数组,时间O(1)。 -
边角块的处理:每个边角块的长度最多为 “块大小
s”(s = n/t,因为总块数为t,总长度n,故每块约n/t个元素)。 -
对边角块的每个元素,需用
get函数(二分查找)计算其在[l, r]中的出现次数,单次二分时间O(log n)。因此,单次查询处理边角块的时间为O(s * log n)(边角块总长度O(s))。 -
总共有
Q次查询,因此总查询时间为O(Q * s * log n)。
目标:平衡预处理与查询时间
块大小 s = n/t(因为总块数 t,总长度 n),代入查询时间表达式得:
总查询时间 = O(Q * (n/t) * log n)。
预处理时间 = O(t * n)。
为了让整体复杂度最优,需让预处理时间与查询时间 “数量级相当”(即两者平衡),即:
t * n ≈ Q * (n/t) * log n
约去 n 后化简:
t² ≈ Q * log n
因此:
t ≈ sqrt(Q * log n)
为什么这样设置更优?
若选择传统的 t = sqrt(n)(块大小 s = sqrt(n)):
-
预处理时间 =
O(sqrt(n) * n) = O(n^(3/2)) -
查询时间 =
O(Q * sqrt(n) * log n)
当 Q 较大(如 Q ~ n)时,查询时间会远大于预处理时间(O(n * sqrt(n) * log n) 远大于 O(n^(3/2))),整体复杂度由查询主导,效率较低。
而选择 t = sqrt(Q * log n) 时:
-
预处理时间 =
O(sqrt(Q * log n) * n) -
查询时间 =
O(Q * (n / sqrt(Q * log n)) * log n) = O(n * sqrt(Q * log n))
两者数量级相同,整体复杂度达到最优平衡,尤其在 Q 较大时优势明显。
总结
块数 t = sqrt(Q * log2(n)) 的设置,是通过数学推导让预处理时间与查询时间平衡,从而最小化整体复杂度,这是针对 “众数查询” 问题(查询次数多、单次查询处理边角块耗时)的优化选择。

浙公网安备 33010602011771号