Atcoder Beginner Contest 107 D Median of Medians(二分答案)(中位数)

题意

给出一个序列,求这个序列所有子序列中位数的中位数

思路

直接考虑非常fake

换个角度,某个数要成为中位数的中位数,那么中位数大于等于他的序列必须占总序列数的至少1/2,而最终答案是符合这个条件的最大的数。于是二分答案。

如何写check函数呢?因为选出mid之后,序列中所有数的具体数值已经不重要了,所以可以把所有大于等于mid的数看作1,小于的看作-1,则某个序列的中位数如果大于等于mid,那这个区间里1和-1的和大于等于0。这里如果对1和-1的序列求前缀和,那么问题转化成求d[i]<=d[j],且i < j的对数,可以用归并或者树状数组求逆序对的方法做。

要注意的是各种等于能不能取到,各种向上取整向下取整,还有区间的le, ri和二分的l, r

另一个中位数题:nowcoder

代码

//归并写法
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long 
using namespace std;
const ll INF = 1e18+10;
const ll N = 100010;
ll n, a[N], lim, d[N], c[N], res;

void chkMax(ll &x, ll y){if (x < y) x = y;}
void chkMin(ll &x, ll y){if (x > y) x = y;}

void Msort(ll l, ll r)
{
    if (l == r) return;
    ll mid = (l+r)>>1;
    Msort(l, mid);
    Msort(mid+1, r);
    ll i = l, j = mid+1, k = l;
    while (i <= mid && j <= r){
        if (d[i] <= d[j]){
            c[k++] = d[i++];
            res += r-j+1;
        }
        else
            c[k++] = d[j++];
    }
    while (i <= mid) c[k++] = d[i++];
    while (j <= r) c[k++] = d[j++];
    for (ll o = l; o <= r; o++)
        d[o] = c[o];
}

bool Check(ll mid)
{
    d[0] = 0;
    for (ll i = 1; i <= n; i++){
        d[i] = d[i-1];
        if (a[i] >= mid)
            d[i]++;
        else
            d[i]--;
    }
    res = 0;
    Msort(0, n);
    if (res >= lim) return true;
    return false;
}

int main()
{
    cin >> n;
    lim = (n*(n+1)/2+1)/2;
    ll l = INF, r = -INF, mid, ans;
    for (ll i = 1; i <= n; i++){
        cin >> a[i];
        chkMax(r, a[i]);
        chkMin(l, a[i]);
    }
    while (l <= r){
        mid = (l+r)>>1;
        if (Check(mid))
            ans = mid, l = mid+1;
        else
            r = mid-1;
    }
    cout << ans;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/82950459