【洛谷P2617】Dynamic Rankings

题目大意:维护带修改区间 K 小值。

题解:学习到了树状数组套权值线段树。
主席树,即:可持久化权值线段树,支持维护静态区间的 K 小值问题,其核心思想是维护 N 棵权值线段树,每个线段树维护的是序列 [1,i] 的权值,并根据可持久化思想使得空间复杂度维持在 \(O(nlogn)\)
树状数组套权值线段树,支持维护带修改区间 K 小值的问题,其核心思想是改变静态主席树中各个权值线段树的前缀和处理方式,在这里采用树状数组中的前缀和处理方式,平衡了修改和查询的时间和空间。时间和空间复杂度为 \(O(nlognlogn)\)。注意:这里 log 是以 2 为底的对数。1e5 的 log2 大约为16,因此内存开 300 倍。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;

inline int read(){
    int x=0,f=1;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
    do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
    return f*x;
}

char opt[5];
int n,m,a[maxn],tmp[2][20],cnt[2];
int d[maxn<<1],len;
struct operation{bool tag;int l,r,k;int pos,val;}q[maxn];
struct node{
    #define ls(x) t[x].lc
    #define rs(x) t[x].rc
    int lc,rc,sum;
}t[maxn*300];
int tot,root[maxn];
inline void pushup(int o){t[o].sum=t[ls(o)].sum+t[rs(o)].sum;}
void insert(int &o,int l,int r,int pos,int val){
    if(!o)o=++tot;
    if(l==r){t[o].sum+=val;return;}
    int mid=l+r>>1;
    if(pos<=mid)insert(ls(o),l,mid,pos,val);
    else insert(rs(o),mid+1,r,pos,val);
    pushup(o);
}
int query(int l,int r,int k){
    if(l==r)return l;
    int mid=l+r>>1;
    int lsize=0;
    for(int i=1;i<=cnt[1];i++)lsize+=t[ls(tmp[1][i])].sum;
    for(int i=1;i<=cnt[0];i++)lsize-=t[ls(tmp[0][i])].sum;
    if(k<=lsize){
        for(int i=1;i<=cnt[1];i++)tmp[1][i]=ls(tmp[1][i]);
        for(int i=1;i<=cnt[0];i++)tmp[0][i]=ls(tmp[0][i]);
        return query(l,mid,k);
    }else{
        for(int i=1;i<=cnt[1];i++)tmp[1][i]=rs(tmp[1][i]);
        for(int i=1;i<=cnt[0];i++)tmp[0][i]=rs(tmp[0][i]);
        return query(mid+1,r,k-lsize);
    }
}
inline int lowbit(int x){return x&-x;}
inline void add(int x,int val){
    int pos=lower_bound(d+1,d+len+1,a[x])-d;
    for(int i=x;i<=n;i+=lowbit(i))insert(root[i],1,len,pos,val);
}
int querykth(int l,int r,int k){
    memset(tmp,0,sizeof(tmp)),cnt[1]=cnt[0]=0;
    for(int i=r;i;i-=lowbit(i))tmp[1][++cnt[1]]=root[i];
    for(int i=l-1;i;i-=lowbit(i))tmp[0][++cnt[0]]=root[i];
    return query(1,len,k);
}

void read_and_parse(){
    n=read(),m=read();
    for(int i=1;i<=n;i++)d[++len]=a[i]=read();
    for(int i=1;i<=m;i++){
        scanf("%s",opt);
        if(opt[0]=='Q')q[i].tag=0,q[i].l=read(),q[i].r=read(),q[i].k=read();
        else q[i].tag=1,q[i].pos=read(),d[++len]=q[i].val=read();
    }
    sort(d+1,d+len+1);
    len=unique(d+1,d+len+1)-d-1;
    for(int i=1;i<=n;i++)add(i,1);
}

void solve(){
    for(int i=1;i<=m;i++){
        if(q[i].tag){
            add(q[i].pos,-1);
            a[q[i].pos]=q[i].val;
            add(q[i].pos,1);
        }else{
            printf("%d\n",d[querykth(q[i].l,q[i].r,q[i].k)]);
        }
    }
}

int main(){
    read_and_parse();
    solve();
    return 0;
}

转载于:https://www.cnblogs.com/wzj-xhjbk/p/10463097.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值