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


    题意

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

    题解

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

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

    考虑如何求解 (cnt)

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

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

    ( ext{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
    ",b[ans]);
    	return 0;
    }
    
  • 相关阅读:
    vue根据不同命令打出不同环境的包
    classpath到底指的哪里
    guava的事件发布订阅功能
    枚举类型的使用
    SpringBoot自动配置的实现原理
    HttpConnection的使用
    SpringBoot下的值注入
    SpringBoot下的Job定时任务
    SpringBoot拦截器的使用
    SpringBoot+MyBatis简单数据访问应用
  • 原文地址:https://www.cnblogs.com/syc233/p/13970496.html
Copyright © 2020-2023  润新知