1. 问题背景与核心思路这道题目来自USACO竞赛的普及级别考察的是树状数组Binary Indexed Tree, BIT在统计问题中的灵活应用。题目要求统计满足特定中位数条件的子数组数量属于经典算法题目的变种。先理解题目核心给定一个长度为N的整数序列和整数X我们需要统计有多少个连续子序列满足其中位数至少为X。根据题目定义长度为M的子序列的中位数是排序后第⌈M/2⌉个数。关键提示中位数至少为X等价于子序列中至少有⌈M/2⌉个数≥X。这个转化是解题的突破口。传统暴力解法需要检查所有O(N²)个子序列对于N≤1e5的数据规模显然不可行。我们需要找到O(N log N)的优化方法这正是树状数组大显身手的地方。2. 算法设计与数学建模2.1 问题转化技巧首先进行关键转化将原数组A转换为标志数组B其中B[i] (A[i] ≥ X) ? 1 : -1。这样子序列的中位数≥X就等价于该子序列的B数组和≥0。例如 原数组A [3, 1, 4, 1, 5] 设X3则B [1, -1, 1, -1, 1]子数组A[1..3] [3,1,4] → B[1..3] [1,-1,1] 和为1≥0确实中位数3≥32.2 前缀和与逆序对思想定义前缀和数组S其中S[0]0S[i]S[i-1]B[i]。那么子数组B[i..j]的和就是S[j]-S[i-1]。我们需要统计满足S[j]-S[i-1]≥0的(i,j)对数即S[j]≥S[i-1]对ji-1。这类似于逆序对问题可以用树状数组高效统计。2.3 离散化处理由于S的值可能很大且不连续需要先离散化。将所有S值排序去重后建立映射将原始值转换为紧凑的整数索引。3. 树状数组实现细节3.1 数据结构初始化树状数组通常实现为以下操作class BIT { private: vectorint tree; public: BIT(int n) : tree(n1) {} void update(int i, int delta) { for(; itree.size(); ii-i) tree[i]delta; } int query(int i) { int res0; for(; i0; i-i-i) restree[i]; return res; } };3.2 统计过程分步解析计算前缀和数组S对S数组进行离散化处理初始化树状数组大小等于离散化后的值域按顺序处理每个S[i]查询当前树状数组中≤S[i]的数的个数将S[i]插入树状数组累加所有查询结果即为答案3.3 边界条件处理特别注意S[0]0需要预先插入树状数组。离散化时要包含所有可能的前缀和值。4. 完整代码实现与注释#include bits/stdc.h using namespace std; class BIT { vectorint tree; public: BIT(int n) : tree(n1) {} void update(int i, int v1) { for(; itree.size(); ii-i) tree[i]v; } int query(int i) { int res0; for(; i0; i-i-i) restree[i]; return res; } }; int main() { int N, X; cin N X; vectorint A(N), B(N), S(N1); for(int i0; iN; i) { cin A[i]; B[i] (A[i] X) ? 1 : -1; } // 计算前缀和 S[0] 0; for(int i1; iN; i) S[i] S[i-1] B[i-1]; // 离散化 vectorint vals S; sort(vals.begin(), vals.end()); vals.erase(unique(vals.begin(), vals.end()), vals.end()); // 建立值到索引的映射 auto get_idx [](int v) { return lower_bound(vals.begin(), vals.end(), v) - vals.begin() 1; }; BIT bit(vals.size()); long long ans 0; // 预先插入S[0] bit.update(get_idx(S[0])); for(int i1; iN; i) { int idx get_idx(S[i]); ans bit.query(idx); bit.update(idx); } cout ans endl; return 0; }5. 复杂度分析与优化空间时间复杂度O(N log N)前缀和计算O(N)离散化排序O(N log N)树状数组操作O(N log N)空间复杂度O(N)存储前缀和和离散化数组优化方向使用哈希表替代离散化但常数可能更大合并离散化和树状数组操作步骤6. 常见错误与调试技巧6.1 典型错误案例忘记处理S[0]导致统计漏掉以第一个元素开头的子数组离散化索引处理不当可能产生0或越界索引整数溢出当N较大时答案可能超过int范围6.2 调试建议打印中间变量特别是前缀和数组和离散化后的索引小数据测试手动计算预期结果验证边界测试全大于X和全小于X的情况关键检查点确保树状数组的大小足够容纳离散化后的所有可能值通常取2*N1比较安全。7. 算法扩展与变种思考求中位数恰好为X的子数组数量可以转化为统计中位数≥X的数量减去中位数≥X1的数量二维情况下的扩展在矩阵中寻找满足条件的子矩阵需要更复杂的数据结构在线查询版本如果X是动态变化的可以考虑可持久化数据结构这种将中位数条件转化为前缀和统计的思路还可以应用于其他百分位数的统计问题。树状数组在此类问题中的高效性使其成为处理大规模数据统计问题的利器。在实际编码竞赛中熟练掌握树状数组的各种应用场景可以显著提升解题效率。建议通过类似题目如逆序对、区间和统计等问题加深理解。
