数组区间查询:求0~L中大于A[R]的元素和的高效解法问询
解决方案:前缀区间内大于指定值的元素和查询
针对问题中1≤N,Q≤10⁵的规模,以下是几种高效的实现方案,覆盖离线、在线两种场景:
一、离线处理 + 树状数组(最优解)
这是时间复杂度最优的方案,适合可以离线收集所有查询的场景。
核心思路
通过将查询按L从小到大排序,逐步将数组元素加入树状数组,确保处理每个查询时,树状数组恰好包含0~L的所有元素。利用树状数组快速计算≤A[R]的元素总和,用前缀总总和减去该值即可得到大于A[R]的元素和。
具体步骤
- 预处理查询:将所有查询存储为三元组
(L, R, idx),其中idx记录原查询的序号,用于最后按原顺序输出结果。 - 排序查询:将查询按
L升序排列。 - 初始化数据结构:维护两个树状数组:
sum_tree:维护对应值的元素总和,支持单点更新和前缀查询。cnt_tree:维护对应值的元素个数(可选,也可直接用sum_tree查询最大值得到总总和)。
- 遍历处理:
- 用指针
ptr标记当前已加入树状数组的元素范围(初始为0)。 - 对每个排序后的查询:
- 若
ptr ≤ L,将A[ptr]插入树状数组(sum_tree添加A[ptr],cnt_tree添加1),并将ptr递增,直到ptr > L。 - 计算
0~L的总元素和:total = sum_tree.query(100000)(因A[i]≤1e5)。 - 计算
≤A[R]的元素和:less_sum = sum_tree.query(A[R])。 - 当前查询的答案为
total - less_sum,存入结果数组的idx位置。
- 若
- 用指针
- 输出结果:按原查询顺序输出结果数组。
复杂度分析
- 时间:O((N+Q)logM),其中M是A[i]的最大值(1e5),完全满足1e5规模的性能要求。
- 空间:O(M),树状数组的空间开销为1e5级别,可接受。
离散化适配
若A[i]范围超过1e5(如1e9),需先对所有A[i]和查询中的A[R]进行离散化,将值映射到1~K的范围(K为不同值的数量),再使用树状数组。
二、Mo算法的优化(解决原有瓶颈)
若必须使用Mo算法,可通过调整块大小、排序策略和维护方式解决超时问题。
核心优化点
- 块大小选择:使用
sqrt(N logN)作为块大小(约400左右,针对1e5),比单纯的sqrt(N)更优。 - 排序策略:采用奇偶排序——块号为偶数时按
R升序,奇数时按R降序,减少指针移动的总次数。 - 高效维护区间信息:放弃树状数组,改用值范围分块维护元素和:
- 将A[i]的范围(1~1e5)分成若干块(如每块300个值)。
- 维护
block_sum(每个块的元素总和)和val_sum(单个值的元素总和)。 - 添加/删除元素时,更新对应
val_sum和block_sum,时间复杂度O(1)。 - 查询
≤A[R]的和时,遍历前几个完整块累加block_sum,再遍历剩余单个值累加val_sum,时间复杂度O(sqrt(M))。
复杂度分析
- 时间:O((N+Q)sqrt(N) + Qsqrt(M)),对于1e5规模,操作数约为6e7,优化后可通过时间限制。
三、线段树(在线查询方案)
适合需要在线处理查询的场景,无需提前收集所有查询。
核心思路
线段树的每个节点维护对应区间内元素的有序列表和前缀和数组:
- 构建时,叶子节点存储单个元素,内部节点通过归并左右子节点的有序列表得到自身的有序列表,并计算前缀和。
- 查询时,将
0~L拆分为线段树的若干节点,对每个节点的有序列表二分查找第一个大于A[R]的位置,用该节点的总元素和减去前缀和得到该节点内符合条件的元素和,最后累加所有节点的结果。
具体步骤
- 构建线段树:
- 每个节点存储
sorted_nums(有序元素列表)和prefix_sums(对应前缀和数组)。 - 内部节点的
sorted_nums是左右子节点sorted_nums的归并结果,prefix_sums是sorted_nums的前缀累加。
- 每个节点存储
- 查询处理:
- 递归拆分
0~L为线段树的节点区间。 - 对每个节点,用二分查找找到
sorted_nums中第一个大于A[R]的索引pos。 - 该节点的贡献为
节点总元素和 - (pos>0 ? prefix_sums[pos-1] : 0)。 - 累加所有节点的贡献得到最终答案。
- 递归拆分
复杂度分析
- 构建:O(N logN),归并操作的总时间为O(N logN)。
- 查询:O(logN * logK),其中K为节点区间的元素个数,每次查询约200次操作,1e5次查询完全可行。
- 空间:O(N logN),存储所有节点的有序列表,约1.7e6空间,可接受。
测试用例验证
以N=4,A=[6,7,2,5]为例:
- 查询(2,2):L=2,A[R]=2。
0~2的元素为6、7、2,总总和15,≤2的和为2,15-2=13,符合输出。 - 查询(3,1):L=3,A[R]=7。
0~3的元素中无大于7的值,结果为0,符合输出。
内容的提问来源于stack exchange,提问作者ANURAG RANJAN
相关产品推荐
相关产品推荐

