Loading

题解「中位数之中位数 median」

题意

求序列的所有子区间的中位数所组成的可重集的中位数。

题解

考虑如何求一个子区间的中位数:考虑二分答案,假设当前二分出的中位数为 \(mid\) ,求出这个子区间内比 \(mid\) 小的数的个数 \(cnt\) ,若 \(cnt\) 大于区间长度的一半,那么中位数比 \(mid\) 小,否则比 \(mid\) 大。

求所有子区间的中位数的中位数也可以使用这种方法。先二分答案 \(mid\) ,求出区间内小于等于 \(mid\) 的数的个数小于等于子区间个数的一半的子区间个数 \(cnt\),若 \(cnt\) 大于所有子区间个数,则中位数比 \(mid\) 小,否则比 \(mid\) 大。

考虑如何求解 \(cnt\)

做一个前缀和 \(p_i\) ,表示前 \(i\) 个数中小于等于 \(mid\) 的数的个数。那么若区间 \([l+1,r]\) 的中位数小于等于 \(mid\) ,有 \(2\times (p_r-p_l)>r-l\) ,移项得 \(2\times p_r-r > 2\times p_l-l\) 。这是经典的逆序对问题,可以通过树状数组求解。

另外有一处细节:每次check的时候要先加入 \(2\times p_0-0\) ,否则统计不到区间 \([1,i]\) 的答案。然而原题数据太水了,没有卡这种情况,加了hack数据后 这篇博客 和两份赛时AC的代码均被hack了。

\(\text{Code}:\)

#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long lxl;
const int maxn=1e5+5;

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
	x*=f;
}

int n,a[maxn];
int b[maxn],m;

namespace BIT
{
	int sum[maxn<<1];
	inline int lowbit(int x) {return x&-x;}
	inline void add(int x,int d)
	{
		for(int i=x;i<=(2*n+1);i+=lowbit(i))
			sum[i]+=d;
	}
	inline int query(int x)
	{
		int res=0;
		for(int i=x;i>=1;i-=lowbit(i))
			res+=sum[i];
		return res;
	}
	inline void clear()
	{
		memset(sum,0,sizeof(int)*(2*n+5));
	}
}

int p[maxn];

inline lxl check(int x)
{
	lxl res=0;
	BIT::clear();
	for(int i=1;i<=n;++i)
		p[i]=p[i-1]+(a[i]<=x);
	BIT::add(n+1,1);
	for(int i=1;i<=n;++i)
	{
		res+=BIT::query(2*p[i]-i-1+n+1);
		BIT::add(2*p[i]-i+n+1,1);
	}
	return res;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("median.in","r",stdin);
	freopen("median.out","w",stdout);
#endif
	read(n);
	for(int i=1;i<=n;++i)
		read(a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	m=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;++i)
		a[i]=lower_bound(b+1,b+m+1,a[i])-b;
	int l=1,r=m,ans=-1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid)>1ll*n*(n+1)/4) r=mid-1,ans=mid;
		else l=mid+1;
	}
	printf("%d\n",b[ans]);
	return 0;
}
posted @ 2020-11-13 17:30  GoPoux  阅读(381)  评论(0编辑  收藏  举报