题解 P2617 【Dynamic Ranking】

· · 题解

这题做法很多,最多的就是树状数组套主席树,这里就不再赘述,上个代码以示敬意

c++

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
inline int read(){
    int sum(0),f(1);
    char ch(getchar());
    for(;ch<'0'||ch>'9';ch=getchar())
        if(ch=='-')
            f=-1;
    for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar());
    return sum*f;
}
inline int lowbit(int x){
    return x&-x;
}
int n,m;
int v[10005],num[20005];
int top,size,cnt;
int rt[10005],lch[20000005],rch[20000005],sum[20000005];
int A[10005],B[10005],K[10005];
int a,b,L[30],R[30];
bool flag[10005];
char op[2];
inline void update(int &x,int las,int pos,int w,int l,int r){
    x=++cnt;
    lch[x]=lch[las];
    rch[x]=rch[las];
    sum[x]=sum[las]+w;
    if(l==r)return;
    int mid((l+r)>>1);
    if(pos<=mid)
        update(lch[x],lch[las],pos,w,l,mid);
    else
        update(rch[x],rch[las],pos,w,mid+1,r);
}
inline int query(int l,int r,int k){
    if(l==r)return l;
    int mid((l+r)>>1),suml(0),sumr(0);
    for(int i=1;i<=a;++i)
        suml+=sum[lch[L[i]]];
    for(int i=1;i<=b;++i)
        sumr+=sum[lch[R[i]]];
    if(k<=sumr-suml){
        for(int i=1;i<=a;++i)
            L[i]=lch[L[i]];
        for(int i=1;i<=b;++i)
            R[i]=lch[R[i]];
        return query(l,mid,k);
    }
    else{
        for(int i=1;i<=a;++i)
            L[i]=rch[L[i]];
        for(int i=1;i<=b;++i)
            R[i]=rch[R[i]];
        return query(mid+1,r,k-(sumr-suml));
    }
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i){
        v[i]=read();
        num[++top]=v[i];
    }
    for(int i=1;i<=m;++i){
        scanf("%s",op);
        A[i]=read();
        B[i]=read();
        if(op[0]=='Q'){
            flag[i]=1;
            K[i]=read();
        }
        else
            num[++top]=B[i];
    }
    sort(num+1,num+top+1);
    size=unique(num+1,num+top+1)-num-1;
    for(int i=1;i<=n;++i){
        v[i]=lower_bound(num+1,num+top+1,v[i])-num;
        for(int j=i;j<=n;j+=lowbit(j))
            update(rt[j],rt[j],v[i],1,1,size);
    }
    for(int i=1;i<=m;++i){
        if(flag[i]){
            a=b=0;
            --A[i];
            for(int j=A[i];j;j-=lowbit(j))
                L[++a]=rt[j];
            for(int j=B[i];j;j-=lowbit(j))
                R[++b]=rt[j];
            printf("%d\n",num[query(1,size,K[i])]);
        }
        else{
            int tmp(v[A[i]]);
            for(int j=A[i];j<=n;j+=lowbit(j))
                update(rt[j],rt[j],tmp,-1,1,size);
            tmp=v[A[i]]=lower_bound(num+1,num+size+1,B[i])-num;
            for(int j=A[i];j<=n;j+=lowbit(j))
                update(rt[j],rt[j],tmp,1,1,size);
        }
    }
}

然而肯定不止这种做法,比如说线段树套平衡树

我们建好一棵线段树,但线段树的每一个节点都是一棵平衡树,查询时二分权值查找,根据二分出的权值在区间中的排名进行二分边界的调整

c++

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<ctime>
using namespace std;
inline int read(){
    int sum(0),f(1);
    char ch(getchar());
    for(;ch<'0'||ch>'9';ch=getchar())
        if(ch=='-')
            f=-1;
    for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar());
    return sum*f;
}
#define get_size(x) (x?x->size:0)
struct node{
    node *lch,*rch;
    int size,key,fix;
    node(int x=0):lch(NULL),rch(NULL),size(1),key(x),fix(rand()){}
    inline void pushup(){
        this->size=get_size(this->lch)+get_size(this->rch)+1;
    }
}*tr[40005];
int n,m;
int a[10005];
char op[2];
inline void left_rotate(node *&x){
    node *tmp(x->rch);
    x->rch=tmp->lch;
    tmp->lch=x;
    x->pushup();
    tmp->pushup();
    x=tmp;
}
inline void right_rotate(node *&x){
    node *tmp(x->lch);
    x->lch=tmp->rch;
    tmp->rch=x;
    x->pushup();
    tmp->pushup();
    x=tmp;
}
inline void insert(node *&x,int v){
    if(!x){
        x=new node(v);
        return;
    }
    if(v<=x->key){
        insert(x->lch,v);
        x->pushup();
        if(x->lch->fix<x->fix)
            right_rotate(x);
    }
    else{
        insert(x->rch,v);
        x->pushup();
        if(x->rch->fix<x->fix)
            left_rotate(x);
    }
}
inline void del(node *&x,int v){
    if(x->key==v){
        if(x->lch&&x->rch){
            if(x->lch->fix<x->rch->fix){
                right_rotate(x);
                del(x->rch,v);
            }
            else{
                left_rotate(x);
                del(x->lch,v);
            }
        }
        else{
            node *tmp(NULL);
            if(x->lch)
                tmp=x->lch;
            else
                tmp=x->rch;
            delete x;
            x=tmp;
        }
    }
    else{
        if(v<=x->key)
            del(x->lch,v);
        else
            del(x->rch,v);
    }
    if(x)
        x->pushup();
}
inline int Rank(node *now,int x){
    int ret(0);
    while(now){
        if(x<=now->key)
            now=now->lch;
        else
            ret+=get_size(now->lch)+1,now=now->rch;
    }
    return ret;
}
inline int kth(node *now,int k){
    while(now){
        if(get_size(now->lch)+1==k)
            return now->key;
        if(get_size(now->lch)+1>=k)
            now=now->lch;
        else
            k-=get_size(now->lch)+1,now=now->rch;
    }
}
inline void build(int l,int r,int rt){
    for(int i=l;i<=r;++i)
        insert(tr[rt],a[i]);
    if(l==r)return;
    int mid((l+r)>>1);
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}
inline void update(int pos,int w,int l,int r,int i){
    del(tr[i],a[pos]);
    insert(tr[i],w);
    if(l==r)return;
    int mid((l+r)>>1);
    if(pos<=mid)
        update(pos,w,l,mid,i<<1);
    else
        update(pos,w,mid+1,r,i<<1|1);
}
inline int Rank(int ll,int rr,int x,int l,int r,int i){
    if(ll<=l&&r<=rr)
        return Rank(tr[i],x);
    int mid((l+r)>>1),ret(0);
    if(ll<=mid)
        ret+=Rank(ll,rr,x,l,mid,i<<1);
    if(mid<rr)
        ret+=Rank(ll,rr,x,mid+1,r,i<<1|1);
    return ret;
}
inline int query(int l,int r,int k){
    int ll(1),rr(1e9);
    while(ll<=rr){
        int mid((ll+rr)>>1);
        int jud(Rank(l,r,mid,1,n,1));
        if(jud<k)
            ll=mid+1;
        else
            rr=mid-1;
    }
    return rr;
}
int main(){
    srand(time(NULL));
    n=read(),m=read();
    for(int i=1;i<=n;++i)
        a[i]=read();
    build(1,n,1);
    while(m--){
        scanf("%s",op);
        if(op[0]=='Q'){
            int x(read()),y(read()),k(read());
            printf("%d\n",query(x,y,k));
        }
        else{
            int x(read()),y(read());
            update(x,y,1,n,1);
            a[x]=y;
        }
    }
}

两种做法比较:

树状数组套主席树:103行 代码长度 2.2KB 时间 232ms 内存 26.21MB(可能是我开大了QAQ) 打代码时间 10min~15min

线段树套平衡树:171行 代码长度 3.07KB 时间 640ms 内存 8.72MB(动态开内存比较准) 打代码时间 15min~20min(应该用不到20min吧,可能是我没打熟QAQ)