题意:
给你一个长度为n的数组,问你长度大于等于k的子串中的中位数最大是多少
题解:
上次做到过一道求平均数最大的,但是我忘记怎么做了,这次又花了一点时间去想。那么感觉这种题目都有一个通解,就是二分+线段树。
首先是二分答案,然后在线段树上区间+当前值是否大于等于mid,那么线段树的叶子就表示这个后缀的中位数是>=mid还是<mid。接下来就查询1~i-k+1这个区间的最大值是否大于0即可。
那么对于平均数的题目的话,我们可以区间+a[i]-mid,然后查询1~i-k+1这个区间的最大值是否>=0即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int a[N],mx[N*4],f[N*4];
void push_down(int root){
if(!f[root])return ;
mx[root<<1]+=f[root];
mx[root<<1|1]+=f[root];
f[root<<1]+=f[root];
f[root<<1|1]+=f[root];
f[root]=0;
}
void update(int l,int r,int root,int ql,int qr,int v){
if(l>=ql&&r<=qr){
mx[root]+=v;
f[root]+=v;
return ;
}
int mid=l+r>>1;
push_down(root);
if(mid>=ql)
update(l,mid,root<<1,ql,qr,v);
if(mid<qr)
update(mid+1,r,root<<1|1,ql,qr,v);
mx[root]=max(mx[root<<1],mx[root<<1|1]);
}
int query(int l,int r,int root,int ql,int qr){
if(l>=ql&&r<=qr)return mx[root];
int mid=l+r>>1,ans=-1;
push_down(root);
if(mid>=ql)ans=query(l,mid,root<<1,ql,qr);
if(mid<qr)ans=max(ans,query(mid+1,r,root<<1|1,ql,qr));
return ans;
}
int n,k;
int check(int x){
memset(f,0,sizeof(f)),memset(mx,0,sizeof(mx));
for(int i=1;i<k;i++)update(1,n,1,1,i,a[i]>=x?1:-1);
for(int i=k;i<=n;i++){
update(1,n,1,1,i,a[i]>=x?1:-1);
if(query(1,n,1,1,i-k+1)>0)return 1;
}
return 0;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int l=1,r=n,mid,ans;
while(r>=l){
mid=l+r>>1;
if(check(mid))l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}