中位数之中位数「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();
}