模板 树状数组套主席树查询动态区间k小

本文介绍了一种结合树状数组与主席树的数据结构优化方案,用于解决动态区间KK小问题。通过对原始数组进行预处理并利用树状数组的特性,实现了高效更新与查询操作。该方法能够将单次查询的时间复杂度降低到O(log²n),整体时间复杂度为O(nlog²n)。
模板,动态区间kkk小:

给定一个长度为nnn的数组,mmm次操作,每次操作只可能是下面其中之一:

(1)给定l,r,kl,r,kl,r,k,求区间[l,r][l,r][l,r]kkk小元素

(2)给定x,yx,yx,y,把a[x]a[x]a[x]改为yyy

树状数组套主席树解法:

让我们回顾一下区间静态区间kkk小的方法,利用主席树可以融合多颗线段树的性质,保存iii的前缀权值线段树,做差获得[l,r][l,r][l,r]的权值线段树,直接在树上二分。显然,iii的前缀权值线段树仅需要在i−1i-1i1的前缀权值线段树上修改即可得到。总而言之,获得[l,r][l,r][l,r]的权值线段树,再在树上二分就是本质思想。

不妨在上面的思路上继续思考,我们把a[x]a[x]a[x]改成yyy,那么只需要在[x,n][x,n][x,n]的前缀上都修改一遍即可达成目的,可是这样的时间复杂度是O(nmlogn)O(nmlogn)O(nmlogn)的,mmm是修改次数,太慢

再进一步,我们能不能加速这个修改过程?由于是前缀线段树,并且存在区间求和,于是我们考虑树状数组,令树状数组每个点都是一个线段树,即主席树上的一个根,如果我们需要修改xxx位置的,能将原来单次修改的时间复杂度由O(nlogn)O(nlogn)O(nlogn)优化为O(log2n)O(log^2n)O(log2n)。那考虑如何获得差值权值线段树,在树状数组上,我们的区间求和策略是获得他们的前缀和做差,但由于树状数组存的是根,没有直接的前缀和,于是,每个根我们只存放一些链,也就是部分的线段树,当我们求得前缀和后,所有的链被合并在一起,成为一颗完整的线段树,这样就可以做差获得差值的权值线段树了

由于我们存放的是根,我们在合并后的线段树上二分,合并之后的线段树不好保存,不妨直接在部分树上分别二分,权值取所有对应点的总和,因此需要一个数组来保存他们的根,并且只有根满足lowbitlowbitlowbit规则(因为只有根在树状数组上),所以预先处理处根的数组,然后树上二分时所有点同时向左/右子树移动。在线段树上二分的时间复杂度时O(logn)O(logn)O(logn),同时二分O(logn)O(logn)O(logn)条,于是单词查询时间复杂度是O(log2n)O(log^{2}n)O(log2n),总时间复杂度是O(nlog2n)O(nlog^{2}n)O(nlog2n)。每次操作在一个根上最多插入一条长O(logn)O(logn)O(logn)的链,每次操作修改了O(logn)O(logn)O(logn)个根,于是总空间复杂度是O(nlog2n)O(nlog^{2}n)O(nlog2n)

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

int n,m;

struct question
{
    string s;
    int l,r,k;
}q[100005]; 

int a[100005],vv[200005];
int tree[40000005],cnt,version[100005];
int lson[40000005],rson[40000005],vp,len;

inline int lowbit(int x){return -x&x;}
inline int& ls(int x){return lson[x];}
inline int& rs(int x){return rson[x];}
inline void push_up(int x){tree[x]=tree[ls(x)]+tree[rs(x)];}

void update(int nl,int l,int r,int x,int k)
{
    if(l==r){tree[x]+=k;return;}
    int mid=l+r>>1;
    if(nl<=mid)
    {
        if(!ls(x)) ls(x)=++cnt;
        update(nl,l,mid,ls(x),k);
    }
    else
    {
        if(!rs(x)) rs(x)=++cnt;
        update(nl,mid+1,r,rs(x),k);
    }
    push_up(x);
}

void add(int x,int k,int v)
{
    while(x<=n)
    {
        if(!version[x]) version[x]=++cnt;
        update(k,1,len,version[x],v);
        x+=lowbit(x);
    }
}

int tmp1[100005],pp1,tmp2[100005],pp2;

int solve(int l,int r,int x,int k)
{
    // printf("x=%d\n",x);
    if(l==r) return l;
    int mid=l+r>>1,sum=0;
    for(int i=1;i<=pp1;i++) sum-=tree[ls(tmp1[i])];
    for(int i=1;i<=pp2;i++) sum+=tree[ls(tmp2[i])];
    if(k<=sum)
    {
        for(int i=1;i<=pp1;i++) tmp1[i]=ls(tmp1[i]);
        for(int i=1;i<=pp2;i++) tmp2[i]=ls(tmp2[i]);
        return solve(l,mid,ls(x),k);
    }
    else
    {
        for(int i=1;i<=pp1;i++) tmp1[i]=rs(tmp1[i]);
        for(int i=1;i<=pp2;i++) tmp2[i]=rs(tmp2[i]);
        return solve(mid+1,r,rs(x),k-sum);
    }
}

int solve(int l,int r,int k)
{
    pp1=pp2=0;
    for(int i=l-1;i;i-=lowbit(i)) tmp1[++pp1]=version[i];
    for(int i=r;i;i-=lowbit(i)) tmp2[++pp2]=version[i];
    return vv[solve(1,len,1,k)];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        vv[++len]=a[i];
    }
    for(int i=1;i<=m;i++)
    {
        auto& [s,l,r,k]=q[i]; cin>>s;
        if(s=="Q") cin>>l>>r>>k;
        else
        {
            cin>>l>>r;
            vv[++len]=r;
        }
    }
    sort(vv+1,vv+1+len); len=unique(vv+1,vv+1+len)-vv-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(vv+1,vv+1+len,a[i])-vv;
    for(int i=1;i<=m;i++)
        if(q[i].s=="C") q[i].r=lower_bound(vv+1,vv+1+len,q[i].r)-vv;
    for(int i=1;i<=n;i++) add(i,a[i],1);
    for(int i=1;i<=m;i++)
    {
        auto& [s,l,r,k]=q[i];
        if(s=="Q") printf("%d\n",solve(l,r,k));
        else
        {
            add(l,a[l],-1);
            add(l,a[l]=r,1);
        }
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值