[ABC107D] Median of Medians

考虑二分答案。于是现在问题转化成了:S 的中位数是否 xx 为二分的值)。

可单纯这样,还没法直接做。继续转化:求出中位数 x 的区间个数。假设我们求出了这个区间个数(设它为 cnt),通过数学归纳,不难得出 S 的中位数 x 当且仅当 cnt(n+1)×n÷22

对于现在的问题,不妨采用一种常见的套路:当所有数与 x 只有大小关系的贡献时,可以把大小满足条件的赋值为 1,不满足条件的赋值为 1;在本题中,所有 <x 的位置赋值为 1,所有 x 的位置赋值为 1。我们要求的有进一步转化为了:和 0 的区间个数。

考虑求一个前缀和(记 1i 的和为 si)。一个 i 作为右端点的某个区间和 0,当且仅当它的左端点 j 满足 sjsi。这是一个二维偏序问题,使用树状数组/线段树求解即可。

#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, a[N], s[N]; ll num;
struct BIT{
    int n = 0, c[N];
    void clear(){FL(i, 0, n) c[i] = 0;}
    void resize(int t){FL(i, n + 1, t) c[i] = 0; n = t;}
    void add(int x, int v){
        for(; x <= n; x += x & -x) c[x] += v;
    }
    int ask(int x, int ret = 0){
        for(; x; x -= x & -x) ret += c[x];
        return ret;
    }
} b;
int check(int x){
    ll cnt = 0; b.clear();
    FL(i, 1, n) s[i] = s[i - 1] + (a[i] < x? -1 : 1);
    FL(i, 0, n) cnt += b.ask(s[i]), b.add(s[i], 1);
    return (cnt << 1) >= num;
}
int main(){
    scanf("%d", &n), num = 1ll * (s[0] = n + 1) * n >> 1;
    FL(i, 1, n) scanf("%d", &a[i]);
    int l = 0, r = 1e9, ans; b.resize(n << 1 | 1);
    while(l <= r){
        int mid = l + r >> 1;
        if(check(mid)) l = (ans = mid) + 1;
        else r = mid - 1;
    }
    printf("%d\n", ans);
    return 0;
}
posted @   徐子洋  阅读(14)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示