Max Median
题意
给出一个长度为 (n) 的序列 (a),你能任选一个区间长度区不小于 (k) 的区间,那么在这些的区间中能得到的区间中位数最大值是多少?
此题中位数指:有 (n) 个数,第 (lfloor frac{n+1}{2} floor) 个数记为中位数。
数据范围:(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 << "
";
return 0;
}