传送门:洛谷 P1168
题目描述
给出一个长度为 N N N的非负整数序列 A i A_i Ai,对于所有 1 ≤ k ≤ ( n + 1 ) / 2 1 \leq k \leq (n+1) / 2 1≤k≤(n+1)/2,输出 A 1 , A 3 , … , A 2 k − 1 A_1, A_3, …, A_{2k - 1} A1,A3,…,A2k−1的中位数。即前 1 , 3 , 5 , … 1,3,5,\dots 1,3,5,…个数的中位数。
分析
题目简单明了,就是求中位数,而且保证是个奇数序列。
中位数,即排序后最中间的数。最暴力的解法就是直接
s
o
r
t
sort
sort一遍,但是,如果动态的询问,那会显的有点吃力。怎么办呢?(这不是平衡树吗? --某大佬言)
但最近在树上发现了一种更为简单的算法—对顶堆(动态维护序列第
k
k
k值)
简单的来说就是利用两个堆(一个大根堆,一个小根堆)来维护序列,保证第
k
k
k值在其中一个堆的堆顶。
就中位数而言:
令大根堆
Q
1
Q1
Q1,小根堆
Q
2
Q2
Q2
- 加入一个元素
x
x
x
放入:若 x x x大于 Q 1 Q1 Q1的堆顶元素,则放入 Q 2 Q2 Q2,否则放入 Q 1 Q1 Q1
调整:若 a b s ( Q 1. s z i e ( ) − Q 2. s z i e ( ) ) = 2 abs(Q1.szie() - Q2.szie()) = 2 abs(Q1.szie()−Q2.szie())=2,将其中一方的堆顶元素放入另一个堆中 - 取两者个数较多的堆顶元素
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#define IL inline
using namespace std;
IL int read()
{
char c = getchar();
int sum = 0 ,k = 1;
for(;'0' > c || c > '9'; c = getchar())
if(c == '-') k = -1;
for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
return sum * k;
}
struct node
{
priority_queue<int>Q1;
priority_queue<int, vector<int>, greater<int> > Q2;
int t1, t2;
IL node()
{
t1 = t2 = 0;
}
IL void add(int x)
{
if(!t1) { ++t1; Q1.push(x); return ; }
if(x > Q1.top()) { Q2.push(x); ++t2; } else { Q1.push(x); ++t1; }
if(t2 - t1 > 1) { Q1.push(Q2.top()); Q2.pop(); --t2; ++t1; } else
if(t1 - t2 > 1) { Q2.push(Q1.top()); Q1.pop(); --t1; ++t2; }
}
IL int get_mid()
{
if(t1 ^ t2) return t1 < t2 ? Q2.top() : Q1.top();
return Q1.top();
}
}a;
int main()
{
int n = read();
for(int i = 1; i <= n; ++i)
{
a.add(read());
if(i & 1) printf("%d\n", a.get_mid());
}
return 0;
}