洛谷 P1168 中位数

题目链接

我的思路和大部分题解一样,都是先把a数组存下来,然后排序去重,记录到b数组里,然后用b数组建一颗线段树。但是具体实现上略微有些不同。

线段树数组存的是它维护区间的左端点(也就是区间最小值)\(l\)和右端点\(r\)(区间最大值)以及本身和子树所存的数字的个数\(num\)

这样当我们add的时候,如果插入的数x比左子树的右端点大,就去找右子树,找到对应位置后,就将\(num++\)

再查找中位数的时候,如果\(rank<=\)当前节点的\(num\),就去查找左子树,\(else\),就将\(rank-\)左子树的\(num\),让后去查右子树。

  • 代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ls p<<1
    #define rs p<<1|1
    #define mid ((l+r)>>1)
    using namespace std;
    int a[100010],b[100010];
    //======线段树及其相关操作
    struct zzz{
    int num,l,r;
    }tree[100010<<2];
    inline void up(int p){
    tree[p].num=tree[ls].num+tree[rs].num; //维护当前节点所存的数字个数
    tree[p].l=tree[ls].l; tree[p].r=tree[rs].r; //维护左右端点
    }
    void build(int l,int r,int p){  //建树
    if(l==r){
        tree[p].r=tree[p].l=b[l];
        return ;    
    }
    build(l,mid,ls); build(mid+1,r,rs);
    up(p);
    }
    void update(int p,int k){  //单点修改
    if(tree[p].l==tree[p].r&&tree[p].l==k){ //如果找到对应位置
        tree[p].num++; //它存的数字个数++
        return ;
    }
    if(k>tree[ls].r) update(rs,k); //如果插入的数x比左子树的右端点大,就去找右子树
    else update(ls,k); //else,就去找左子树
    up(p);
    }
    int ans(int l,int r,int p,int rank){ //查找中位数
    int zzz=0;
    if(l==r)
      return tree[p].l;
    if(rank<=tree[ls].num)  //如果k<=当前节点的num
      zzz=ans(l,mid,ls,rank); //查找左子树
    else{
        rank-=tree[ls].num;  //否则,将rank-左子树的的num
        zzz=ans(mid+1,r,rs,rank); //去找右子树
    }
    return zzz;
    }
    //=======快读
    int read(){
    int k=0; char c=getchar();
    for(;c<'0'||c>'9';) c=getchar();
    for(;c>='0'&&c<='9';c=getchar())
      k=(k<<3)+(k<<1)+c-48;
    return k;
    }
    int main(){
    int n=read();
    for(int i=1;i<=n;i++) b[i]=a[i]=read();
    sort(b+1,b+n+1); //排序
    int sz=unique(b+1,b+n+1)-(b+1); //去重
    build(1,sz,1);
    for(int i=1;i<=n;i++){
        update(1,a[i]);
        if(i&1)
          printf("%d\n",ans(1,sz,1,(i+1)/2));
    }
    return 0;
    }

猜你喜欢

转载自www.cnblogs.com/wxl-Ezio/p/9181268.html