NowCoder 172A 中位数

本文介绍了一种通过二分搜索和前缀和方法解决寻找序列中所有长度大于等于指定值的子区间最大中位数的问题,并给出了两种实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接:https://www.nowcoder.com/acm/contest/172/A
来源:牛客网

题目描述
小N得到了一个非常神奇的序列A。这个序列长度为N,下标从1开始。A的一个子区间对应一个序列,可以由数对[l,r]表示,代表A[l], A[l + 1], …, A[r]这段数。对于一个序列B[1], B[2], …, B[k],定义B的中位数如下:

  1. 先对B排序。得到新的序列C。
  2. 假如k是奇数,那么中位数为。假如k为偶数,中位数为。
    对于A的所有的子区间,小N可以知道它们对应的中位数。现在小N想知道,所有长度>=Len的子区间中,中位数最大可以是多少。
    输入描述:
    第一行输入两个数N,Len。
    第二行输入序列A,第i个数代表A[i]。
    输出描述:
    一行一个整数,代表所有长度>=Len的子区间中,最大的中位数。
    示例1
    输入
    11 3
    4864 8684 9511 8557 1122 1234 953 9819 101 1137 1759
    输出
    8684

数据范围:
30%: n <= 200
60%: n <= 2000
另外有20%:不超过50个不同的数
100%:1<=Len<=n<=10^5, 1 <= a[i] <= 10^9


原本的60分代码:
暴力枚举区间+对顶栈维护动态中位数

#include<cstdio>
#include<queue>
using namespace std;
int a[100005],ans,n,len;
priority_queue <int> q1,q2;
inline void read(int &x)
{
	char ch;
	while((ch=getchar())<'0'||ch>'9');
	x=ch-'0';
	while((ch=getchar())>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0';
}
int main()
{
	read(n);read(len);
	for(int i=1;i<=n;i++)read(a[i]);
	for(int i=1;i<=n;i++)
	{
		while(q1.size())q1.pop();
		while(q2.size())q2.pop();
		for(int j=i;j<=n;j++)
		{ 
			if(q1.empty()||a[j]<q1.top())q1.push(a[j]);
			else q2.push(-a[j]); 
			if(q1.size()>(j-i+2)/2)
			{
				q2.push(-q1.top());
				q1.pop();
			}
			if(q2.size()>(j-i+1)/2)
			{
				q1.push(-q2.top());
				q2.pop();
			}
			if(j-i+1>=len&&ans<q1.top())ans=q1.top();
		}
	 }
	printf("%d",ans);
	return 0; 
} 

中位数接触的不多,第一次看到这种操作(果然还是太菜了):
二分+前缀和+前缀最大值维护
下附AC代码

#include<cstdio>
using namespace std;
int a[100005],b[100005],n,len,ans;
inline void read(int &x)
{
	char ch;
	while((ch=getchar())<'0'||ch>'9');
	x=ch-'0';
	while((ch=getchar())>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0';
}
bool cherk(int x)
{
	for(int i=1;i<=n;i++)
	if(a[i]<x)b[i]=-1;else b[i]=1;
	for(int i=1,mi=0;i<=n;i++)
	{
		if(i>len&&mi>b[i-len])mi=b[i-len];
		b[i]+=b[i-1];
		if(i>=len&&b[i]-mi>0)return 1;
	}
	return 0;
} 
int main()
{
	read(n);read(len);
	for(int i=1;i<=n;i++)read(a[i]);
	int l=0,r=int(1e9);
	while(l<r)
	{
		int mid=1LL*(l+r+1)>>1;
		if(cherk(mid))l=mid;else r=mid-1;
	}
	printf("%d",l);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值