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))$

原文地址:https://www.cnblogs.com/sto324/p/11409183.html