洛谷 P1168 中位数

 P1168 中位数

题目描述

给出一个长度为N的非负整数序列 Ai ,对于所有 1 ≤ k ≤ (N + 1) / 2 

输出 A1,A3,,A2k - 1的中位数。即前 1,3,5,… 个数的中位数。

 

输入输出格式

输入格式:

1行为一个正整数 N,表示了序列长度。

2行包含N个非负整数 Ai (Ai ≤ 10^9) 

 

输出格式:

(N + 1) / 2 行,第 i 行为 A1,A3,,A2k - 1 的中位数。

 

输入输出样例

输入样例#1: 复制
7
1 3 5 7 9 11 6
输出样例#1: 复制
1
3
5
6

说明

对于 20% 的数据,N ≤ 100

对于 40% 的数据,N ≤ 3000

对于 100% 的数据,N ≤ 100000



这道题乍一看比较简单 其实还是有不少细节的 

我第一想法是建立主席树 然后每次插数的时候就查询中位数个数那么大的数就好了

是查询区间第k大的板子嘛 时间复杂度是 $O(nlogn)$ 

然而这道题O(nlogn2)可以很容易的搞过去 所以在wans的怂恿下写了值域树状数组套二分...写完才发现并不是那么容易的

第一个问题就是数据是 $ 1e9 $ 范围 显然需要离散化 然后离散化完了还要映射回去 

然后就是二分的问题 每次二分查找前面的数的个数 如果前面的数的个数包括自己刚好等于中位数的个数

就先更新答案 然后 r=mid-1  因为有可能我找到了一个空值 就比如 $10101000$ 而我找到的是后面的 直接返回就会出问题

第二个是当我找到的值大于我中位数的位数时 一样需要更新答案 因为我一个数可能出现多次 我找到的是大于中位数排名的个数

但是这时候这个排名可能恰好处于这个数所在的区间 但是因为我们计算加上了整个区间的个数  所以这时候也要更新答案

还有就是 $unique$ 记得 - 1 !!!!!!

代码

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

const int N = 1e5 + 5;
int n,m,c[N],val[N],a[N],b[N],ma;

int lowbit(int pos) {
    
    return pos & (-pos);
}

void add(int pos,int del) {
    
    while(pos <= m) {
        c[pos] += del;
        pos += lowbit(pos);
    }
}

int query(int pos) {
    
    int ans = 0;
    while(pos >= 1) {
        ans += c[pos];
        pos -= lowbit(pos);
    }
    return ans;
}

void solve(int num,int pos) {
    
    int l = 1,r = pos,ans = num;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(query(mid) == num) {
            ans = mid;
            r = mid - 1;
        }
        else if(query(mid) < num) l = mid + 1;
        else if(query(mid) > num) ans = mid,r = mid - 1;
    }
    printf("%d\n",val[ans]);
}

int main( ) {
    
    scanf("%d",& n);
    for(int i = 1;i <= n;i ++) {
        scanf("%d",& a[i]);
        b[i] = a[i];
    }
    sort(a + 1,a + n + 1);
    m = unique(a + 1,a + n + 1) - a - 1;
    for(int i = 1;i <= n;i ++) {
        int pos = lower_bound(a + 1,a + m + 1,b[i]) - a;
        val[pos] = b[i];
    }
    for(int i = 1;i <= n;i ++) {
        int pos = lower_bound(a + 1,a + m + 1,b[i]) - a;
        add(pos,1);
        int nn = query(m);
        if(i % 2 == 1) solve((nn + 1) / 2,m);
    }
}

 

posted @ 2018-09-01 20:43  阿澈说他也想好好学习  阅读(126)  评论(0编辑  收藏  举报