双map实现的加强版对顶堆

新功能

维护一个动态数组,支持插入、删除、和查询中位数
传统大小堆的对顶堆维护不了删除

双map对顶堆代码

拜读了@StarSilk的代码,感觉非常牛,于是在博客记录下来

#include <bits/stdc++.h>
#define debug(x) cerr<<#x<<':'<<x<<'\n';
#define ll long long
using namespace std;
//加强版的对顶堆
/*
	维护中位数

	对顶堆
	1. 插入
	2. 查询中位数

	双map
	1. 插入
	2. 删除
	3. 查询中位数
*/
const ll inf = 1e18;
int n, q, cl, cr;
ll a[200005];
map<ll, int> mpl, mpr;

void solve();
void ins(ll x);
int chkans();
ll lmax();
ll rmin();
void rmv(ll x);

void rmv(ll x) {
	if (x <= lmax()) {
		mpl[x]--;
		cl--;
	} else {
		mpr[x]--;
		cr--;
	}

	while (cl > cr + 1) {
		ll y = lmax();
		mpr[y]++;
		mpl[y]--;
		cr++;
		cl--;
	}
	while (cl < cr) {
		ll y = rmin();
		mpr[y]--;
		mpl[y]++;
		cr--;
		cl++;
	}
}

int chkans() {
	ll x = rmin();
	return cl - mpl[x];
}

ll lmax() { //查询左map最大值
	map<ll, int>::iterator it;
	while (1) {
		if (mpl.empty())
			return -inf;
		it = mpl.end();
		--it;
		if ((*it).second == 0) {
			mpl.erase(it);
			continue;
		}
		return (*it).first;
	}
	return -inf;
}

ll rmin() { //查询右map最小值
	map<ll, int>::iterator it;
	while (1) {
		if (mpr.empty())
			return inf;
		it = mpr.begin();
		if ((*it).second == 0) {
			mpr.erase(it);
			continue;
		}
		return (*it).first;
	}
	return inf;
}

void ins(ll x) {
	if (x < lmax()) { //比左map最大值小
		cl++;
		mpl[x]++;
	} else { //否则加入右map
		cr++;
		mpr[x]++;
	}
	//维护cl<=cr+1,cl==cr二选一
	while (cl > cr + 1) {
		ll y = lmax();
		mpl[y]--;
		mpr[y]++;
		cl--;
		cr++;
	}
	while (cl < cr) {
		ll y = rmin();
		mpr[y]--;
		mpl[y]++;
		cl++;
		cr--;
	}
}

void solve() {
	cin >> n >> q;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		ins(a[i]);
	}
	for (int i = 1, u; i <= q; i++) {
		ll d;
		cin >> u >> d;
		rmv(a[u]);
		a[u] += d;
		ins(a[u]);

		cout << chkans() << '\n';
	}

	mpl.clear(), mpr.clear();
	cl = cr = 0;
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	int T = 1;
	cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2025-08-19 18:40  Gusare  阅读(5)  评论(0)    收藏  举报