中位数之中位数「NOIP多校联考 2019」

题意

给出一个长度为n的序列a,首先求出其所有区间的中位数,将这些中位数构成的集合记为S,求S中所有数的中位数。

这里定义的中位数指: 对于m个数,将其从小到大排序后,第(m/2+1)个数即为中位数,例如(10,30,20)的中位数为20,(10,30,20,40)的中位数为30,(10,10,10,20,30)的中位数为10


思路

这道题还是比较巧妙的。(orz \(mhy\),\(zhh\)

我们可以二分答案,这个显然是有单调性的。那么只要求出值不超过枚举的mid的中位数的数量即可。

我们定义数组\(p[i]\)表示前i个数中超过mid的数的数量。

那么每一个符合条件的区间就会满足:\(r-l>2*(p[r]-p[l])\)(超过mid的数的数量没有达到区间总数的一半)

移一下项得到\(r-2*p[r]>l-2*p[l]\)

于是我们就将其转换为一个逆序对问题了,每一次check的时候将i-p[i]插入树状数组中然后统计即可。

代码

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

	template<typename T>inline void read (T &x) {
		x=0;T f=1;char c=getchar();
		for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
		for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
		x*=f;
	}

	template<typename T>inline void write (T x) {
		if (x<0) putchar('-'),x*=-1;
		if (x>=10) write(x/10);
		putchar(x%10+'0');
	}

}

using namespace StandardIO;

namespace Project {
	#define int long long
	
	const int N=100100;
	
	int n;
	int a[N],p[N],llim=0x3f3f3f3f3f3f,rlim;
	int tree[N<<2];
	
	inline int lowbit (int x) {
		return x&(-x);
	}
	inline void update (int x,int v) {
		for (register int i=x; i<n*4; i+=lowbit(i)) tree[i]+=v;
	}
	inline int query (int x) {
		int res=0;
		for (register int i=x; i; i-=lowbit(i)) res+=tree[i];
		return res;
	}
	inline bool check (int x) {
		int res=0;memset(tree,0,sizeof(tree));
		for (register int i=1; i<=n; ++i) 
			p[i]=p[i-1]+2*(a[i]>x);
//		update(n,1);
		for (register int i=1; i<=n; ++i) {
			update(i-p[i]+2*n,1);
			res+=query(i-p[i]-1+2*n);
		}
		return (res>=n*(n+1)/4);
	}
	
	inline void MAIN () {
		read(n);
		for (register int i=1; i<=n; ++i) {
			read(a[i]),llim=min(llim,a[i]),rlim=max(rlim,a[i]);
		}
		int mid,ans;
		while (llim<=rlim) {
			mid=(llim+rlim)>>1;
			if (check(mid)) rlim=mid-1,ans=mid;
			else llim=mid+1;
		}
		write(ans);
	}
	
	#undef int
}

int main () {
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	Project::MAIN();
}

posted @ 2019-11-02 23:09  Ilverene  阅读(430)  评论(0编辑  收藏  举报