[洛谷 P1168] 中位数 --- 对顶堆

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27121257/article/details/82855962

传送门:洛谷 P1168


题目描述

给出一个长度为 N N 的非负整数序列 A i A_i ,对于所有 1 k ( n + 1 ) / 2 1 \leq k \leq (n+1) / 2 ,输出 A 1 , A 3 , , A 2 k 1 A_1, A_3, …, A_{2k - 1} 的中位数。即前 1 , 3 , 5 , 1,3,5,\dots 个数的中位数。


分析

题目简单明了,就是求中位数,而且保证是个奇数序列。
中位数,即排序后最中间的数。最暴力的解法就是直接 s o r t sort 一遍,但是,如果动态的询问,那会显的有点吃力。怎么办呢?(这不是平衡树吗? --某大佬言)
但最近在树上发现了一种更为简单的算法—对顶堆(动态维护序列第 k k 值)
简单的来说就是利用两个堆(一个大根堆,一个小根堆)来维护序列,保证第 k k 值在其中一个堆的堆顶。
就中位数而言:
令大根堆 Q 1 Q1 ,小根堆 Q 2 Q2

  • 加入一个元素 x x
    放入:若 x x 大于 Q 1 Q1 的堆顶元素,则放入 Q 2 Q2 ,否则放入 Q 1 Q1
    调整:若 a b s ( Q 1. s z i e ( ) Q 2. s z i e ( ) ) = 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;
}

猜你喜欢

转载自blog.csdn.net/qq_27121257/article/details/82855962