24. AtCoder-Median of Medians

题目链接:Median of Medians

经典的二分做法。如果一个数有机会成为中位数,那么比它小的数不超过 ⌈ n 2 ⌉ \lceil \frac{n}{2}\rceil 2n 个,比它大的数也不超过 ⌈ n 2 ⌉ \lceil \frac{n}{2}\rceil 2n 个。所以我们可以这样验证一个数是否有机会成为答案:将原序列中小于这个数的都变成 − 1 -1 1,其他的数变成 1 1 1,如果某个子区间的和是非负数,说明这个子区间原来的中位数小于等于我们检测的数。我们可以用树状数组 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的求出所有这种子区间的数量 c n t cnt cnt。由于子区间的总数量是 n ( n + 1 ) 2 \dfrac{n(n+1)}{2} 2n(n+1),如果 c n t ≥ n ( n + 1 ) 4 cnt\ge \dfrac{n(n+1)}{4} cnt4n(n+1),说明这个数有机会成为答案,接下来检查更大的值,否则说明这个数太大必不可能成为答案,往小的值去找。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 2e5 + 5;
const ll mod = 1e9 + 7;
ll bit[maxn];
int lowbit(int x) {
	return x & -x;
}
void add(int x, int k) {
	for (int i = x; i < maxn; i += lowbit(i))
		bit[i] += k;
}
ll sum(int x) {
	ll ret = 0;
	for (int i = x; i > 0; i -= lowbit(i))
		ret += bit[i];
	return ret;
}

ll a[maxn], b[maxn];
ll n;
ll t[maxn];
bool check(ll x) {
	memset(bit, 0, sizeof(bit));
	for (int i = 1; i <= n; ++i) {
		t[i] = a[i] >= x ? 1 : -1;
	}
	ll ans = 0;
	add(n, 1);
	for (int i = 1; i <= n; ++i) {
		t[i] += t[i - 1];
		ans += sum(t[i] + n);
		add(t[i] + n, 1);
	}
	return (ans >= n * (n + 1) / 4);
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin >> n;
	for (int i = 1; i <= n; ++i)
		cin >> a[i];
	for (int i = 1; i <= n; ++i)
		b[i] = a[i];
	sort(b + 1, b + n + 1);
	int tot = unique(b + 1, b + n + 1) - b - 1;
	int l = 1, r = tot, ans;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(b[mid]))
			l = mid + 1, ans = b[mid];	
		else 
			r = mid - 1;
	}
	cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值