中位数之最

Max Median

题意

给出一个长度为 \(n\) 的序列 \(a\),你能任选一个区间长度区不小于 \(k\) 的区间,那么在这些的区间中能得到的区间中位数最大值是多少?

此题中位数指:有 \(n\) 个数,第 \(\lfloor \frac{n+1}{2} \rfloor\) 个数记为中位数。

数据范围:\(1≤k≤n≤2*10^5,1≤a_i≤n\)

思路

把所以满足条件的区间中位数都放入一个集合中,集合的最大值即为答案。

通过枚举的方式得到集合肯定是不行的,但可以通过对集合内 $ >x$ 的元素计数的方式来确定一个数在集合中的位置,比如若集合最大值 \(\leq x\),那么集合中 \(>x\) 的元素个数为 \(0\) ,否则集合中 \(>x\) 的元素个数不为 0,显然 \(x\) 具有单调性,通过计数结果可以对 \(x\) 的位置进行检验。

计数方法,设 \(p_i\)\([1,i]\)\(\leq x\) 的数的个数。如果区间 \([l, r]\) 的中位数 \(>x\),可以得到 \(r-(l-1) > 2*(p_r-p_{l-1})\) ,将式子简化为 \(r-2*p_r > (l-1) - 2*p_{l-1}\) ,又因为区间长度至少为 \(k\),所以对于右端点 \(r\),它的左端点只能在 \([0,r-k]\) 之间选定,这就可以用树状数组进行计数。

代码

#include<bits/stdc++.h>

using namespace std;

const int maxn = 2e5+1;
int a[maxn], b[maxn], n, k;
int p[maxn], q[maxn], tree[maxn<<1];

void add(int x, int val) {
    for(int i = x; i <= 2*n+1; i += i&-i) {
        tree[i] += val;
    }
}

int sum(int r) {
    int res = 0;
    for(int i = r; i; i -= i&-i) {
        res += tree[i];
    }
    return res;
}

bool check(int x) { // true 为x小于最值
    long long res = 0;
    memset(tree, 0, sizeof tree);
    q[0] = n+1;
    for(int i = 1; i <= n; ++ i) {
        p[i] = p[i-1] + 2*(a[i]<=x);
        q[i] = i-p[i]+n+1;
        if (i < k) continue;
        add(q[i-k], 1);
        res += sum(q[i]-1);
    }
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> k;
    for(int i = 1; i <= n; ++ i) {
        cin >> a[i];
    }
    int l = 1, r = n;
    while(l < r) {
        int mid = l + r >> 1;
        if (check(mid)) l = mid + 1;
        else r = mid;
    }
    cout << l << "\n";
    return 0;
}
posted @ 2021-02-19 20:20  Daowuu  阅读(117)  评论(0编辑  收藏  举报