【洛谷 P3871】[TJOI2010] 中位数 题解(优先队列+对顶堆)

本文详细解析了如何通过优先队列(最大堆和最小堆)计算给定整数序列的中位数,涉及添加元素和查询中位数的操作。
摘要由CSDN通过智能技术生成

[TJOI2010] 中位数

题目描述

给定一个由 N N N 个元素组成的整数序列,现在有两种操作:

  • 1   add   a \texttt{1 add }\textit{a} 1 add a:在该序列的最后添加一个整数 a a a,组成长度为 N + 1 N + 1 N+1 的整数序列。
  • 2   mid \texttt{2 mid} 2 mid:输出当前序列的中位数。

中位数是指将一个序列按照从小到大排序后处在中间位置的数。(若序列长度为偶数,则指处在中间位置的两个数中较小的那个)

1 1 1 [ 1 , 2 , 13 , 14 , 15 , 16 ] [1, 2, 13, 14, 15, 16] [1,2,13,14,15,16] 中位数为 13 13 13
2 2 2 [ 1 , 3 , 5 , 7 , 10 , 11 , 17 ] [1, 3, 5, 7, 10, 11, 17] [1,3,5,7,10,11,17] 中位数为 7 7 7
3 3 3 [ 1 , 1 , 1 , 2 , 3 ] [1, 1, 1, 2, 3] [1,1,1,2,3] 中位数为 1 1 1

输入格式

第一行为初始序列长度 N N N。第二行为 N N N 个整数,表示整数序列,数字之间用空格分隔。第三行为操作数 M M M,即要进行 M M M 次操作。下面为 M M M 行,每行输入格式如题意所述。

输出格式

对于每个 mid \verb!mid! mid 操作输出中位数的值。

样例 #1

样例输入 #1

6
1 2 13 14 15 16
5
add 5
add 3
mid
add 20
mid

样例输出 #1

5
13

提示

数据范围及约定

  • 对于 30 % 30\% 30% 的数据, 1 ≤ N ≤ 10 , 000 1 ≤ N ≤ 10,000 1N10,000 0 ≤ M ≤ 1 , 000 0 ≤ M ≤ 1,000 0M1,000
  • 对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 100 , 000 1 ≤ N ≤ 100,000 1N100,000 0 ≤ M ≤ 10 , 000 0 ≤ M ≤ 10,000 0M10,000

序列中整数的绝对值不超过 1 0 9 10^9 109,序列中的数可能有重复。


思路

中位数是指在一个数列中,如果将这个数列按照从小到大排序,那么位于数列中间位置的数就是这个数列的中位数。对于有偶数个元素的数列,通常的定义是取中间两个数的平均值作为中位数,但在这段代码中,中位数被定义为中间两个数中的较小者。

这个数据结构使用了两个优先队列,一个是最大堆,一个是最小堆。最大堆存储的是数列中较小的一半的数,最小堆存储的是数列中较大的一半的数。这样,最大堆的堆顶就是数列中的中位数。

在插入一个新的数时,首先判断这个数是否小于最小堆的堆顶,如果是,那么就把这个数插入到最大堆中,否则就插入到最小堆中。然后,如果最小堆的大小超过了最大堆,就把最小堆的堆顶元素移动到最大堆中。如果最大堆的大小超过了最小堆的大小加一,就把最大堆的堆顶元素移动到最小堆中。这样可以保证最大堆和最小堆的大小差距不超过一,从而保证最大堆的堆顶是中位数。

在主函数中,首先读入一个整数n,然后读入n个整数并插入到数据结构中。然后读入一个整数m,接着进行m次操作。每次操作是一个字符串,如果字符串是"add",那么就读入一个整数并插入到数据结构中,否则就输出当前的中位数。


AC代码

#include <iostream>
#include <queue>
#define AUTHOR "HEX9CF"
using namespace std;

int n, m;
int cnt = 0;
priority_queue<int> hmax;
priority_queue<int, vector<int>, greater<int>> hmin;

void push(int x) {
	if (!hmin.empty() && x < hmin.top()) {
		hmax.push(x);
	} else {
		hmin.push(x);
	}
	cnt++;
	while (hmin.size() > hmax.size()) {
		hmax.push(hmin.top());
		hmin.pop();
	}
	while (hmax.size() > hmin.size() + 1) {
		hmin.push(hmax.top());
		hmax.pop();
	}
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int t;
		cin >> t;
		push(t);
	}
	cin >> m;
	for (int i = 1; i <= m; i++) {
		string op;
		cin >> op;
		if (op == "add") {
			int t;
			cin >> t;
			push(t);
		} else {
			cout << hmax.top() << endl;
		}
	}
	return 0;
}

  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值