中位数之最
Max Median
题意
给出一个长度为 \(n\) 的序列 \(a\),你能任选一个区间长度区不小于 \(k\) 的区间,那么在这些的区间中能得到的区间中位数最大值是多少?
此题中位数指:有 \(n\) 个数,第 \(\lfloor \frac{n+1}{2} \rfloor\) 个数记为中位数。
数据范围:\(1≤k≤n≤2*10^5,1≤a_i≤n\) 。
思路
把所以满足条件的区间中位数都放入一个集合中,集合的最大值即为答案。
通过枚举的方式得到集合肯定是不行的,但可以通过对集合内 $ >x$ 的元素计数的方式来确定一个数在集合中的位置,比如若集合最大值 \(\leq x\),那么集合中 \(>x\) 的元素个数为 \(0\) ,否则集合中 \(>x\) 的元素个数不为 0,显然 \(x\) 具有单调性,通过计数结果可以对 \(x\) 的位置进行检验。
计数方法,设 \(p_i\) 为 \([1,i]\) 中 \(\leq x\) 的数的个数。如果区间 \([l, r]\) 的中位数 \(>x\),可以得到 \(r-(l-1) > 2*(p_r-p_{l-1})\) ,将式子简化为 \(r-2*p_r > (l-1) - 2*p_{l-1}\) ,又因为区间长度至少为 \(k\),所以对于右端点 \(r\),它的左端点只能在 \([0,r-k]\) 之间选定,这就可以用树状数组进行计数。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+1;
int a[maxn], b[maxn], n, k;
int p[maxn], q[maxn], tree[maxn<<1];
void add(int x, int val) {
for(int i = x; i <= 2*n+1; i += i&-i) {
tree[i] += val;
}
}
int sum(int r) {
int res = 0;
for(int i = r; i; i -= i&-i) {
res += tree[i];
}
return res;
}
bool check(int x) { // true 为x小于最值
long long res = 0;
memset(tree, 0, sizeof tree);
q[0] = n+1;
for(int i = 1; i <= n; ++ i) {
p[i] = p[i-1] + 2*(a[i]<=x);
q[i] = i-p[i]+n+1;
if (i < k) continue;
add(q[i-k], 1);
res += sum(q[i]-1);
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> k;
for(int i = 1; i <= n; ++ i) {
cin >> a[i];
}
int l = 1, r = n;
while(l < r) {
int mid = l + r >> 1;
if (check(mid)) l = mid + 1;
else r = mid;
}
cout << l << "\n";
return 0;
}