题目大意

给定长度为 的整数数组 和整数 ,考虑所有长度 的连续子数组,对每个子数组取其下中位数(偶数长度取两中间较小者),求这些中位数中的最大值

下中位数定义:长度为 的序列排序后取第 个元素(1-indexed)
例如:[2,7,3,5] → 排序 [2,3,5,7] → 中位数 = 第 2 个 = 3

解题思路

核心观察

  • 答案一定是原数组中的某个元素(因为中位数来自子数组元素)
  • 直接枚举所有子数组不可行( 子数组,每组排序 → 超时)

二分答案 + 判定转化

我们二分答案 ,问题转化为:

是否存在一个长度 的子数组,其中位数

如何高效判定?

构造辅助数组

关键性质
子数组的中位数 当且仅当 其对应的 子数组和

证明:设子数组长度 的元素有
中位数

前缀和优化判定

用前缀和 delta[i] = b[0]+...+b[i-1],则子数组 v[l..r] 的和为 delta[r+1] - delta[l]

要判断是否存在长度 的子数组满足和 ,只需:

  • 遍历右端点 (对应前缀和 delta[i]
  • 维护 min_prefix = min(delta[0], ..., delta[i-m])
  • delta[i] - min_prefix >= 1,则存在合法子数组

时间复杂度: 每次判定

算法步骤

  1. 去重排序:将数组元素去重并排序(用于二分候选答案)
  2. 二分答案:在排序后的数组上二分索引
  3. 判定函数 check(x)
    • 构造前缀和数组 delta
    • 维护最小前缀和 min_prefix
    • 若存在 delta[i] - min_prefix > 0(即 ),返回 true
  4. 输出最大可行答案

复杂度分析

  • 时间复杂度

    • 每组测试数据:
      • 使用 set 去重并构建 search 数组:
      • 二分答案:最多 次迭代(因为去重后最多 个不同值)
      • 每次 check 函数:
      • 小计:
    • 题目保证所有测试数据中 的总和不超过
    • 因此总时间复杂度为:
      完全可以通过。
  • 空间复杂度,用于存储数组、前缀和及去重后的候选值。

代码实现

#include <bits/stdc++.h>
using namespace std;

bool check(int x, vector<int>& v, int m) {
    int n = v.size();
    vector<int> delta(n + 1, 0);
    // 构造前缀和:>=x 为 +1,否则 -1
    for (int i = 0; i < n; i++) {
        delta[i + 1] = delta[i] + (v[i] >= x ? 1 : -1);
    }
    
    int min_prefix = 0; // delta[0] = 0
    // 遍历右端点 i(对应子数组结束于 i-1)
    for (int i = 1; i <= n; i++) {
        if (i >= m) {
            // 检查是否存在左端点 j ∈ [0, i-m] 使得 delta[i]-delta[j] >= 1
            if (delta[i] - min_prefix > 0) {
                return true;
            }
            // 更新 min_prefix:加入 delta[i - m + 1](为下一轮准备)
            min_prefix = min(min_prefix, delta[i - m + 1]);
        }
    }
    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int q;
    cin >> q;
    while (q--) {
        int n, m;
        cin >> n >> m;
        vector<int> v(n);
        set<int> st;
        for (int i = 0; i < n; i++) {
            cin >> v[i];
            st.insert(v[i]);
        }
        // 去重并排序
        vector<int> search(st.begin(), st.end());
        int l = 0, r = (int)search.size() - 1;
        int ans = 0;
        
        // 二分答案(在排序后的数组上)
        while (l <= r) {
            int mid = (l + r) / 2;
            if (check(search[mid], v, m)) {
                ans = mid;      // 记录可行答案
                l = mid + 1;    // 尝试更大的值
            } else {
                r = mid - 1;    // 尝试更小的值
            }
        }
        cout << search[ans] << '\n';
    }
    return 0;
}