题目链接:点击这里。
分析题意
题目可以理解为,先给你第一个数,输出他(第一次的中位数),在之后每泵入两个数,对已泵入的所有数取中位数输出。
时间复杂度分析
N N N 最大为 1 0 5 10^5 105,所以复杂度不可能超过 o ( N 2 ) o(N^2) o(N2),大概率是 o ( N × l o g N ) o(N \times logN) o(N×logN)。
思路分析
拿到本题一开始手足无措,不知所云。以为是把输入数组排个序然后输出前 ( N + 1 ) / 2 (N + 1) / 2 (N+1)/2 个数字,于是写了个小顶堆交上去了,接着就 WA 了。其实这是一道 对顶堆 题。
对顶堆
解释
正如他的名字一样,它是由两个堆的堆顶相接拼起来的。如图,保证数据 a[1] < mid < b[1
。虽然两个堆内部可能并非有序,但是我们利用堆顶的性质,a[1]
一定是大顶堆最大的,b[1]
一定是小顶堆最小的,那么我们要找的数让它夹在这两个之间即可。
作用
对顶堆用作于找序列中第k个大(小)的数,简单来说就是找一个有序序列中的第 k k k 个数。
什么时候选择对顶堆
本题为何不用二分呢,二分也是从有序序列中找数啊?
本题多次询问且每次询问的区间不同,所以每次用二分之前需要对数组进行排序,一旦数据量大起来,我们负担不起这个时间消耗,所以我们用对顶堆。
简单来说,对顶堆用于在动态数组中选取第k个数。
AC CODE
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <vector>
#include <algorithm>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int n;
int h[N], a[N], b[N], cnt1, cnt2;
void up1(int u, int h[]){
while(u / 2 && h[u / 2] < h[u]){
swap(h[u / 2], h[u]);
u >>= 1;
}
}
void up2(int u, int h[]){
while(u / 2 && h[u / 2] > h[u]){
swap(h[u / 2], h[u]);
u >>= 1;
}
}
void down1(int u, int h[], int cnt){
int t = u;
if(u * 2 <= cnt && h[u * 2] > h[t]) t = u * 2;
if(u * 2 + 1 <= cnt && h[u * 2 + 1] > h[t]) t = u * 2 + 1;
if(t != u){
swap(h[u], h[t]);
down1(t, h, cnt);
}
}
void down2(int u, int h[], int cnt){
int t = u;
if(u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;
if(u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if(t != u){
swap(h[u], h[t]);
down2(t, h, cnt);
}
}
int main(){
cin >> n;
for(int i = 1; i <= n; ++i) scanf("%d", &h[i]);
int mid = h[1];
cout << h[1];
for(int i = 2; i <= n; ++i){
if(h[i] > mid){
b[++cnt2] = h[i];
up2(cnt2, b);
}else{
a[++cnt1] = h[i];
up1(cnt1, a);
}
if(i % 2 == 1){
while(cnt1 != cnt2){
if(cnt1 < cnt2){
a[++cnt1] = mid;
up1(cnt1, a);
mid = b[1];
b[1] = b[cnt2--];
down2(1, b, cnt2);
}else{
b[++cnt2] = mid;
up2(cnt2, b);
mid = a[1];
a[1] = a[cnt1--];
down1(1, a, cnt1);
}
}
cout << endl << mid;
}
}
}
注意事项
- 本题中有两个不同的堆,所以对不同堆的操作也不同,意思就是说
up, down
要各写两个,不然肯定 WA。笑死。 - 其实不用在几个
up, down
函数中传入堆数组和计数器的,直接各自用各自的就行,只要不用错了。我这么写纯纯因为我是傻逼。