AtCoder Regular Contest 101 (ARC101) D - Median of Medians 二分答案 树状数组

原文链接https://www.cnblogs.com/zhouzhendong/p/ARC101D.html

题目传送门 - ARC101D

题意

  给定一个序列 A 。

  定义一个序列 A 的中位数为:给 A 排序,得到的第 $\left\lfloor\cfrac{i}{2}\right\rfloor+1$ 项的值。

  序列 B 由序列 A 的所有连续子序列的中位数构成。

  问序列 B 的中位数是多少。

  序列中可能出现重复的数,$|A| \leq 10^5$ 。

题解

  注意这里说的“中位数”是题意里面定义的“中位数”。

  首先考虑二分答案。

  对于一个数 $k$ ,我们考虑判断最终序列的中位数是否 大于等于 $k$。

  考虑将原序列中 大于等于 $k$ 的数全部设置为 $1$,小于 $k$ 的数设为 $-1$ 。那么,如果一个序列的和 非负,它原来的中位数就必然大于等于 $k$ 。

  考虑用树状数组统计出中位数大于等于 $k$ 的序列个数 $ans$ 。

  如果 $ans\geq \cfrac{n(n+1)}4$ ,那么显然,序列 B 的中位数大于等于 $k$ 。

  于是就可以在 $O(n\log^2 n)$ 的时间复杂度内解决这个问题了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200005;
int n,a[N],Ha[N],hs,k,f[N];
struct BIT{
int n,c[N];
void set(LL _n){
n=_n;
memset(c,0,sizeof c);
}
void add(int x,int d){
for (;x<=n;x+=x&-x)
c[x]+=d;
}
int ask(int x){
int ans=0;
for (;x;x-=x&-x)
ans+=c[x];
return ans;
}
}T;
bool check(int k){
for (int i=1;i<=n;i++)
f[i]=f[i-1]+(a[i]>=k?1:-1);
T.set(n*2+1);
LL ans=0;
for (int i=0;i<=n;i++){
ans+=T.ask(f[i]+n+1);
T.add(f[i]+n+1,1);
}
return ans>=1LL*n*(n+1)/4;
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),Ha[i]=a[i];
sort(Ha+1,Ha+n+1);
hs=unique(Ha+1,Ha+n+1)-Ha-1;
for (int i=1;i<=n;i++)
a[i]=lower_bound(Ha+1,Ha+hs+1,a[i])-Ha;
int L=1,R=hs,mid,ans=L;
while (L<=R){
mid=(L+R)>>1;
if (check(mid))
L=mid+1,ans=mid;
else
R=mid-1;
}
printf("%d",Ha[ans]);
return 0;
}

  

上一篇:linux下批量查找/替换文本内容


下一篇:边缘检测