• Dynamic Rankings(树状数组+值域线段树)


    题目描述

    给出一个长度为n的序列,有m个操作:询问区间[l,r]中第k小的数,将a[i]改成y

    n,m<=1e5,a[i]<=1e9

    题解

    普通的查询区间第k小就用主席树+值域线段树就好了,但是需要修改的话,考虑主席树就是维护前缀和,修改一个点就需要将之后的都修改,这样就会$n^{2}logn$,T飞。

    换一种方法记录前缀和,用树状数组维护,在树状数组的每个节点维护一个值域线段树,每个节点就维护他所管辖区间的值域线段树,每次修改就只修改logn个节点。查询就用O(logn)个值域线段树一起查询,因为用树状数组求前缀和就是log个节点相加得到。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=100005;
    const int maxm=90000005;
    const int oo=1000000000;
    int n,m,a[maxn];
    int cnt,root[maxn];
    int ls[maxm],rs[maxm],size[maxm];
    
    template<class T>inline void read(T &x){
        x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    }
    
    void modify(int &rt,int l,int r,int pos,int val){
        if(!rt) rt=++cnt;
        size[rt]+=val;
        if(l==r) return ;
        int mid=(l+r)>>1;
        if(pos<=mid) modify(ls[rt],l,mid,pos,val);
        else modify(rs[rt],mid+1,r,pos,val);
    }
    
    void get_modify(int x,int pos,int val){for(;x<=n;x+=x&-x) modify(root[x],0,oo,pos,val);}
    
    int tp1,tp2,s1[maxn],s2[maxn];
    
    int get_query(int x,int y,int k){
        tp1=tp2=0;
        for(;x;x-=x&-x) s1[++tp1]=root[x];
        for(;y;y-=y&-y) s2[++tp2]=root[y];
        int l=0,r=oo;
        while(1){
            if(l==r) return l;
          int sum=0,mid=(l+r)>>1;
          for(int i=1;i<=tp1;i++) sum-=size[ls[s1[i]]];
          for(int i=1;i<=tp2;i++) sum+=size[ls[s2[i]]];
          if(sum>=k){
              for(int i=1;i<=tp1;i++) s1[i]=ls[s1[i]];
              for(int i=1;i<=tp2;i++) s2[i]=ls[s2[i]];
              r=mid;
        }
          else {
              for(int i=1;i<=tp1;i++) s1[i]=rs[s1[i]];
              for(int i=1;i<=tp2;i++) s2[i]=rs[s2[i]];
              l=mid+1;k-=sum;
        }
        }
    }
    
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            read(a[i]);
            get_modify(i,a[i],1);
        }
        for(int i=1;i<=m;i++){
            char op[2];
            scanf("%s",op);
            if(op[0]=='Q') {
                int l,r,k;
                scanf("%d%d%d",&l,&r,&k);
                printf("%d
    ",get_query(l-1,r,k));
            }
            else {
                int x,y;
                scanf("%d%d",&x,&y);
                get_modify(x,a[x],-1);
                get_modify(x,a[x]=y,1);
            }
        }
    }
    Dynamic Rankings

    这里用的也是动态开点。代码还是比较清晰,也比较好写。

    主要理解将在区间查询和修改值域线段树用树状数组维护即可。

    空间复杂度$O(nlog^{2}n))$

  • 相关阅读:
    C#串口通信程序实现无感知签到与答题
    C# 调用adb command 读取手机型号和IMEI
    sql 截取字符串
    .NET下的ORM框架有哪些
    linq-to-sql实现left join,group by,count
    C# 生成二维码
    数据库面试题.net
    .net面试中的一些常见问题与答案
    Jquery判断其中任意一个文本框的值是否被修改
    矩阵乘法的MPI并行计算
  • 原文地址:https://www.cnblogs.com/sto324/p/11409183.html
Copyright © 2020-2023  润新知