【二分】CF1486D Max Median

原题洛谷查看CF1486D Max Median 

题意

一个长度为n(1<=n<=2e5)的序列a,定义中位数为一段连续的数升序排序后中间的那个数。如果长度为偶数len,则中位数为第(len/2向下取整)个数。求序列a所有长度大于等于k(1<=k<=2e5)的连续子序列中,中位数的最大可能的取值。

思路

可以看出,答案具有单调性,因此可以使用二分答案。

那如何判断一个数能否成为一段区间的中位数呢?

观察一些序列的中位数,其实可以发现:在长度为n的序列中,小于中位数的数至多有\left \lfloor \frac{n}{2} \right \rfloor个,至少有\left \lfloor \frac{n}{2} \right \rfloor+1个,总有小于中位数的数小于大于等于中位数的数。一种很奇妙的方法:在二分一个数x时,将序列a中的数转化为1和-1存入mark数组中,即

 再用一个数组s记录mark的前缀和(此处称作状态和,s_{i}表示从1到i的状态和),此时对于区间i到j的状态和为s_{j}-s_{i-1},令其大于0即可!

 

选择枚举s_{j},当s_{j}已知时,此时让s_{i-1}尽量小即可,那就要再维护一个前缀最小值mi数组,即

 代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll z=200005;
ll n,k,mid,l,r,a[z],c[z],s[z],mi[z],ans;
bool check(ll x)
{
	for(int i=1;i<=n;i++)
	{
		if(a[i]>=x)c[i]=1;
		else c[i]=-1;
	}//转化,大于就是1小于就是-1
	for(int i=1;i<=n;i++)
	{
		s[i]=s[i-1]+c[i];//前缀和
		mi[i]=min(mi[i-1],s[i]);//前缀和的前缀最小值
	}
	for(int i=k;i<=n;i++)
	if(s[i]-mi[i-k]>0)return true;//大于0的时候,说明中位数在x之上,推进左边界
	return false;//否则推进右边界
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		r=max(r,a[i]);//找最大数,获取右边界,减少查询次数
	}
	while(l<=r)//二分
	{
		mid=(l+r)/2;
		if(check(mid))ans=mid,l=mid+1;
		else r=mid-1;
	}
	cout<<ans;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值