luogu P1168 中位数 题解

P1168 中位数

这题可以用权值线段树查k-th,可以上平衡树之类

事实上,,这题数据比较水的原因,可以用vector直接水过去。。。

一句话题意:给出一个长度为\(N\)的非负整数序列\(A_i\)

,对于所有\(1≤k≤(N+1)/2\),输出\(A_1,A_3,...,A_{2k-1}\)

的中位数。即前\(1,3,5,…\)个数的中位数。

这里上一个巧妙的线段树做法

首先我们a数组先去重然后存进b数组

然后update的时候记录每个数出现的次数,找到这个数的位置即可。

简单介绍下lower_bound用法:lower_bound(begin,end,x):从数组的begin位置到end-1位置二分查找第一个大于或等于x的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

update:

void update(int p,int w)//w存位置 
{
    t[p].cnt++;//记录这个数的出现次数
    if(t[p].l==t[p].r) return ;
    if(w<=t[p].mid) update(p<<1,w);//t[p].mid=l+r>>1,于build()中
    else update(p<<1|1,w);
}

query这样写:

int query(int p,int w)//w存位置 
//如果节点p的左儿子下有x个数,w>x,
//那么cnt是位于右儿子的第w-x个数。
//否则在左子树寻找第w个。
//这样找下去能保证答案的正确性 
//找中位数的巧妙性质 
{
    if(t[p].l==t[p].r) return t[p].l;
    if(cnt<=t[p<<1].cnt) return query(p<<1,cnt);//左子树找
    else return query(p<<1|1,cnt-t[p<<1].cnt);//右子树找 
}

简单离散化:

    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&a[i]);b[i]=a[i];
    }   
    sort(b+1,b+n+1);
    int k=unique(b+1,b+1+n)-b;

简单更新:

    for(int i=1;i<=n;i++)
    int kk=lower_bound(b+1,b+1+k,a[i])-b,update(1,kk);//找到a[i]在b[]中的位置,并对应更新

然后再输出即可。

   //接上面
    if(i&1)
        printf("%d\n",b[query(1,i/2+1)]);

总结一下

这题还是巧妙在能想到查询的时候在左子树和右子树找。

算是较为灵活的运用。

猜你喜欢

转载自www.cnblogs.com/Ano-Ano/p/12349255.html