带修改的区间第k小(树状数组套主席树)

赵队牛逼!!!!!

题目链接

题目内容

对一个序列进行两种操作:

  • 修改第 i i i个数为 x x x

  • 查询区间 [ l , r ] [l,r] [l,r] k k k小的数

分析

对于不待修改的区间第 k k k小,我们可以用主席树完成。

我们来看看只用主席树如何完成带修改的区间第 k k k小。

对于每次修改,我们都需要把当前位置及以后的主席树都进行修改。因此每次修改的时间复杂度为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n)),空间增加 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n)) n n n次操作直接爆炸。查询操作依旧是 O ( l o g ( n ) ) O(log(n)) O(log(n))

下面考虑加上树状数组后的情况。

我们把树状数组的每个点都改为一棵主席树,树状数组的每次修改操作都需要修改 l o g ( n ) log(n) log(n)个节点,由于每个节点都是主席树,所以每次修改时间复杂度为 O ( l o g 2 ( n ) ) O(log^2(n)) O(log2(n)),空间增加 O ( l o g 2 ( n ) ) O(log^2(n)) O(log2(n)) ,比较优秀。

注:此题还需要离散化

如何实现树状数组套主席树

普通主席树中第 i i i棵主席树维护的是 1 到 i 1到i 1i这段区间的权值信息。

而树状数组套主席树中的第 i i i棵主席树,维护树状数组中第 i i i个点所维护的点的权值信息,即: 在普通树状数组第 7 7 7个点维护 节点 1 , 2 , 4 1,2,4 1,2,4的和。那么树状数组套主席树的第 7 7 7个点就维护节点 1 , 2 , 4 1,2,4 1,2,4的权值信息。

代码

空间要开很大(此处感谢赵队)

只用待修的主席树常数较大,姿势不对就会超时。

正确操作:

普通主席书维护原序列的信息,带修改的主席树维护修改的信息

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 20;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int cnt = 0, S[maxn],T[maxn], L[maxn << 8], R[maxn << 8], sum[maxn << 8];
int build(int l, int r)
{
    int rt = ++cnt;
    sum[rt] = 0;
    if (l < r)
    {
        int mid = (l + r) >> 1;
        L[rt] = build(l, mid);
        R[rt] = build(mid + 1, r);
    }
    return rt;
}
int update(int pre, int l, int r, int x, int val)
{
    int rt = ++cnt;
    L[rt] = L[pre];
    R[rt] = R[pre];
    sum[rt] = sum[pre] + val;
    if (l < r)
    {
        int mid = (l + r) >> 1;
        if (x <= mid)
            L[rt] = update(L[pre], l, mid, x, val);
        else
            R[rt] = update(R[pre], mid + 1, r, x, val);
    }
    return rt;
}
int lowbit(int x)
{
    return x & (-x);
}
int used[maxn],n,m;
void add(int x, int pos, int val)
{
    for (int i = pos; i <= n; i += lowbit(i))
        S[i] = update(S[i], 0, 2e5 + 10, x, val);
}
int query(int t1,int t2,int u, int v, int l, int r, int k)
{
    if (l >= r)
        return l;
    int s = 0, mid = (r + l) >> 1;
    for (int i = v; i > 0; i -= lowbit(i))
        s += sum[L[used[i]]];
    for (int i = u; i > 0; i -= lowbit(i))
    s+=sum[L[t2]]-sum[L[t1]];
    if (s >= k)
    {
        for (int i = v; i > 0; i -= lowbit(i))
            used[i] = L[used[i]];
        for (int i = u; i > 0; i -= lowbit(i))
            used[i] = L[used[i]];
        return query(L[t1],L[t2],u, v, l, mid, k);
    }
    else
    {
        for (int i = v; i > 0; i -= lowbit(i))
            used[i] = R[used[i]];
        for (int i = u; i > 0; i -= lowbit(i))
            used[i] = R[used[i]];
        return query(R[t1],R[t2],u, v, mid + 1, r, k - s);
    }
}
struct node
{
    char id;
    int l,r,data;
}q[maxn];
int book[maxn],h[maxn];
int gethash(int x,int len)
{
    return lower_bound(h,h+len,x)-h;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int now=0;
    T[0] = build(0, 2e5 + 10);
    for(int i=0;i<=n;i++) S[i]=T[0];
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> book[i],h[now++]=book[i];
    for(int i=0;i<m;i++)
    {
        char id;
        cin >> id;
        if (id == 'C')
        {
            int pos, x;
            cin >> pos >> x;h[now++]=x;
            q[i].id='C',q[i].l=pos,q[i].data=x;
        }
        else if (id == 'Q')
        {
            int l, r, k;
            cin >> l >> r >> k;
            q[i].id='Q',q[i].l=l,q[i].r=r,q[i].data=k;
        }
    }
    sort(h,h+now);
    int num=unique(h,h+now)-h;
    for(int i=1;i<=n;i++)T[i]=update(T[i-1],0,2e5+10,gethash(book[i],num),1);
    for(int i=0;i<m;i++)    
    {
        if(q[i].id=='C')
        {
            add(gethash(book[q[i].l],num),q[i].l,-1);
            add(gethash(q[i].data,num),q[i].l,1);
            book[q[i].l]=q[i].data;
        }
        else if(q[i].id=='Q')
        {
            for(int j=q[i].r;j>0;j-=lowbit(j)) used[j]=S[j];
            for(int j=q[i].l-1;j>0;j-=lowbit(j)) used[j]=S[j];
            int pos=query(T[q[i].l-1],T[q[i].r],q[i].l-1,q[i].r,0,2e5+10,q[i].data);
            cout<<h[pos]<<'\n';
        }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值