CF1486D Max Median

Description

给定一个长为 \(n\) 的序列 \(a_i\),求最大的区间中位数,且该区间长度大于 \(k\)

Solution

这道题的转换挺巧妙。

对于一个数 \(x\),若其大于等于一个区间的中位数,当且仅当大于等于它的数的个数 \(>\) 小于它的数个数。这两类在序列中重新赋值,大于等于赋为 1,小于的赋为 -1,那么条件成立当且仅当区间和是正数。所以想到二分一个 \(x\),然后做上述转换,判断存不存在这样的一个区间,那么只需要求出最大子段和。可以前缀和优化,子段和即为 \(s_r-s_{l-1}\) 。固定右端点 \(r\),在 \([1,r-k]\)\(s_{l-1}\) 最小的即可。

#include<stdio.h>

const int N=200007;

inline int read(){
	int x=0,flag=1; char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') flag=0; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
	return flag? x:-x;
}

int n,k,a[N],b[N];

inline int min(int x,int y){return x<y? x:y;}
inline int max(int x,int y){return x>y? x:y;}
bool check(int x){
	for(int i=1;i<=n;i++)
		b[i]=b[i-1]+((a[i]>=x)? 1:-1);
	int ret=0,s=n;
	for(int i=k;i<=n;i++){
		s=min(s,b[i-k]);
		ret=max(ret,b[i]-s);
	}
	return ret>0;
}

int main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++) a[i]=read();
	int l=1,r=n,ans=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)) l=mid+1,ans=mid;
		else r=mid-1;
	}
	printf("%d",ans);
}
posted @ 2021-02-19 11:09  Kreap  阅读(44)  评论(0编辑  收藏  举报