1 条题解
-
0
问题分析
我们需要解决两个主要问题:
- 区间众数查询:对于每个查询区间 ,找到出现次数最多且编号最小的基因(夯基)
- 答案序列处理:将所有查询答案组成序列,求所有连续子区间的 的最大值
算法思路
整体架构
代码根据数据规模采用两种不同的策略:
- 小数据 ():使用分块算法
- 大数据 ():使用树上启发式合并(DSU on Tree)
方法一:分块算法(solve1)
分块预处理
b = sqrt(n), bc = n / b; // 块大小和块数量 for (int i = 0; i <= bc; i++) { bl[i] = i * b, br[i] = (i + 1) * b - 1; }核心思想
-
预处理每块到末尾的众数:
- 对每个块 ,计算从 到 的众数
- 存储在
Zs[i][j]中
-
预处理块内计数:
Ap[i][j]存储前 个块中基因 的出现次数
-
查询处理:
- 同一块内:暴力统计
- 跨块查询:
- 暴力处理左右不完整块
- 中间完整块使用预处理结果
- 合并三部分结果
方法二:树上启发式合并(solve2)
区间树构建
// 根据区间包含关系构建树 while (stk.size() && q[i].l > q[stk.top()].r) stk.pop(); if (stk.empty()) e[0].push_back(i); else e[stk.top()].push_back(i); stk.push(i);DSU on Tree 过程
- 重儿子选择:选择区间长度最大的儿子作为重儿子
- 轻子树处理:递归处理并清空计数
- 重子树处理:保留计数结果
- 合并操作:将当前区间与重儿子区间差异部分加入计数
答案处理(print_final)
问题转化
对于答案序列 ,求:
$$\max_{1 \le l \le r \le m} \left[ \left( \bigwedge_{i=l}^{r} a_i \right) + \left( \sum_{i=l}^{r} a_i \right) \right] $$优化算法
for (int i = 1; i <= m; i++) { for (int j = 0; j <= 20; j++) { if (Ans[i] & (1 << j)) continue; else La[j] = i; // 记录该位为0的最后位置 } for (int j = 0; j <= 21; j++) { if (La[j] == i) continue; // 计算以i为右端点的最优解 } }关键观察:按位与的值由所有数在该位都为1决定,利用这一性质可以高效枚举。
复杂度分析
分块算法(solve1)
- 预处理:
- 单次查询:
- 总复杂度:
树上启发式合并(solve2)
- 树构建:
- DSU on Tree:
- 总复杂度:
算法选择依据
测试点范围 算法选择 原因 分块算法 数据规模较小,分块常数小 树上启发式合并 大数据下更优的复杂度 代码优点
- 自适应算法选择:根据测试点编号自动选择最优算法
- 高效区间处理:利用区间包含关系构建树结构
- 位运算优化:在答案处理中利用位运算性质降低复杂度
- 内存管理:及时清空临时计数数组,避免内存泄漏
代码总结
本题通过结合分块和树上启发式合并两种算法,高效解决了大规模区间众数查询问题。针对不同的数据特征选择最优策略,体现了算法设计的灵活性。答案处理部分的位运算优化更是展现了对于问题性质的深入理解。
该解法能够处理 的大数据规模,适用于所有测试点。
- 1
信息
- ID
- 4617
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者