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);
}