Codeforces Round #703 (Div. 2) D. Max Median

Max Median

在这里插入图片描述
Examples

input

5 3
1 2 3 2 1

output

2

input

4 2
1 2 3 4

output

3

在这里插入图片描述
题目大意

给定一个长度为n的序列a,求a的所有连续且长度大于等于k的子序列里中位数的最大值。

分析

由于1<=ai<=n,因此中位数的取值为1~n,如果已经确定x是一个中位数,那么中位数的最大值一定>=x,这就说明中位数的最大取值具有单调性,考虑用二分来做。设左端点为l,右端点为r,初始条件l=1,r=n,每次取mid=(l+r+1)>>1,问题的关键在于如何判断是否存在一个中位数>=mid,这里用到一个巧妙的做法,将所有小于mid的数标记为-1,大于等于mid的数标记为1,这样就得到了一个只包含1和-1的序列。如果一个区间的中位数>=mid,那么这个区间(转换为1和-1后的区间)的和一定>0,根据这个结论,还需要求一下前缀和,由于序列长度是大于等于k的,因此需要寻找所有长度大于等于k的区间是否有和是大于0的,这里还用到了前缀最小值进行优化。

时间复杂度O(nlog(n))

AC代码

#include <bits/stdc++.h>
using namespace std;

const int N=2e5+10;
int a[N];
int b[N];
int n,k;

bool check(int mid)
{
    
    
	for(int i=1;i<=n;i++)
	{
    
    
		if(a[i]>=mid) b[i]=1;
		else b[i]=-1;
	}
	for(int i=1;i<=n;i++) b[i]+=b[i-1];
	int flag=0;
	int mn=0;  //最小值
	for(int i=k;i<=n;i++)
	{
    
    
		mn=min(mn,b[i-k]);
		if(b[i]-mn>0)
		{
    
    
			flag=1;
			break;
		}
	}
	return flag;
}

int main() 
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    int l=1,r=n;
    while(l<r)
    {
    
    
    	int mid=(l+r+1)>>1;
    	if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l<<endl;
    
    return 0;
}

注意

这里mid=(l+r+1)>>1是为了避免死循环,是由于位运算向下取整引起的。

猜你喜欢

转载自blog.csdn.net/weixin_46155777/article/details/113873722