树状数组套线段树

https://www.luogu.com.cn/problem/P3380

题意

维护数列,支持以下操作:

  • \(op=1\),查询 \(k\)\([l,r]\) 内的排名.

  • \(op=2\),查询区间内排名为 \(k\) 的值.

  • \(op=3\),修改某一位置的值.

  • \(op=4\),查询 \(k\)\([l,r]\) 的前驱.

  • \(op=5\),查询 \(k\)\([l,r]\) 的后继.

\(1\le n,q \le 10^5\).

思路

参考主席树,使用树状数组套线段树维护.

记数组长度为 \(n\),离散化后值域为 \(m\),询问次数为 \(q\).

各个函数:

  • \(update\):对单棵线段树单点修改,时间复杂度 \(\mathcal{O}(\log m)\).

  • \(build\):第 \(i\) 棵线段树维护 \([i-lowbit(i)+1,i]\) 区间信息,暴力 \(update\),时间复杂度 \(\mathcal{O}(n\log n \log m)\),可以用线段树合并优化到 \(\mathcal{O}(n\log m)\).

  • \(query\_range\):查询下标在 \([l,r]\),值域在 \([x,y]\) 范围的元素数量,实现依赖于两个子函数:

    • \(query\_pre\):查询单棵线段树中,值域在 \([1,x]\) 范围内的元素数量,时间复杂度 \(\mathcal{O}(\log m)\).

    • \(query\):查询下标在 \([1,pos]\),值域在 \([x,y]\) 范围内的元素数量. 从 \(pos\) 开始不断减 \(lowbit\),把下标范围内所有线段树根节点取出,累加 \(query\_pre(rt,y)-query\_pre(rt,x-1)\),时间复杂度 \(\mathcal{O}(\log n\log m)\).

    返回 \(query(r,x,y)-query(l-1,x,y)\) 即可,时间复杂度和 \(query\) 相同,\(\mathcal{O}(\log n\log m)\).

  • \(change\):将 \(pos\) 位元素从 \(val\) 改成 \(nval\),从 \(pos\) 开始不断加 \(lowbit\)\(update\) 所有线段树即可,时间复杂度 \(\mathcal{O}(\log n\log m)\).

  • \(queryk\):查询下标在 \([l,r]\) 范围内第 \(k\) 个元素,这和主席树查询区间第 \(k\) 小逻辑是一样的,区别在于主席树只需要维护 \(l-1\)\(r\) 的线段树根,这里需要维护所有 \(l-1\)\(r\) 向下不断减 \(lowbit\) 的线段树根,需要精细实现避免反复开 \(vector\),时间复杂度 \(\mathcal{O}(\log n\log m)\).

空间复杂度理论宽松上界是 \(\mathcal{O}((n+q)\log n\log m)\).

得到了该数据结构,解决原问题是容易的.

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct FenwickSegmentTree{
    struct node{
        int cnt = 0;
        int left=-1,right=-1;
    };

    int n,m;
    vector<node> seg;
    vector<int> root;

    FenwickSegmentTree(int _n,int _m){
        n = _n;
        m = _m;
        root.resize(n+1);
        for (int i=1;i<=n;i++){
            seg.emplace_back();
            root[i] = seg.size()-1;
        }
    }

    void update(int rt,int l,int r,int pos,int val){
        if (l==r){
            seg[rt].cnt += val;
            return;
        }

        int mid = l+r >> 1;
        if (pos<=mid){
            if (seg[rt].left==-1){
                seg.emplace_back();
                seg[rt].left = seg.size()-1;
            }
            update(seg[rt].left,l,mid,pos,val);
        }
        else{
            if (seg[rt].right==-1){
                seg.emplace_back();
                seg[rt].right = seg.size()-1;
            }
            update(seg[rt].right,mid+1,r,pos,val);
        }
        seg[rt].cnt = (seg[rt].left==-1?0:seg[seg[rt].left].cnt)+(seg[rt].right==-1?0:seg[seg[rt].right].cnt);
    }

    void update(int rt,int pos,int val){
        if (pos<1 || pos>m) return;
        update(rt,1,m,pos,val);
    }

    void build(vector<int>& a){
        for (int i=1;i<=n;i++){
            for (int j=i;j<=n;j+=j&-j){
                update(root[j],a[i],1);
            }
        }
    }

    void change(int pos,int val,int nval){
        for (int i=pos;i<=n;i+=i&-i){
            update(root[i],val,-1);
            update(root[i],nval,1);
        }
    }

    int query_pre(int rt,int l,int r,int pos){
        if (rt==-1) return 0;
        if (l==r){
            return seg[rt].cnt;
        }

        int mid = l+r >> 1;
        if (pos<=mid){
            return query_pre(seg[rt].left,l,mid,pos);
        }
        else{
            return (seg[rt].left==-1?0:seg[seg[rt].left].cnt)+query_pre(seg[rt].right,mid+1,r,pos);
        }
    }

    int query_pre(int rt,int val){
        if (val<1 || val>m) return 0;
        return query_pre(rt,1,m,val);
    }

    int query(int pos,int x,int y){
        if (x>y) return 0;
        ll res = 0;
        for (int i=pos;i>=1;i-=i&-i){
            res += query_pre(root[i],y)-query_pre(root[i],x-1);
        }       
        return res;
    }

    int query_range(int l,int r,int x,int y){
        return query(r,x,y)-query(l-1,x,y);
    }

    void push(vector<int>& vec,int op){
        for (auto& rt:vec){
            if (rt==-1) continue;
            rt = op==0?seg[rt].left:seg[rt].right;
        }
    }

    int queryk(vector<int>& rtl,vector<int>& rtr,int l,int r,ll k){
        if (l==r){
            return l;
        }

        int mid = l+r >> 1;
        ll cnt = 0;
        for (auto& rt:rtr){
            if (rt!=-1 && seg[rt].left!=-1){
                cnt += seg[seg[rt].left].cnt;
            }     
        }
        for (auto& rt:rtl){
            if (rt!=-1 && seg[rt].left!=-1){
                cnt -= seg[seg[rt].left].cnt;
            }     
        }

        if (cnt>=k){
            push(rtl,0);
            push(rtr,0);
            return queryk(rtl,rtr,l,mid,k);
        }
        else{
            push(rtl,1);
            push(rtr,1);
            return queryk(rtl,rtr,mid+1,r,k-cnt);
        }
    }

    int queryk(int l,int r,ll k){
        if (l<1 || l>n || r<1 || r>n || l>r || k<1) return -1;
        vector<int> rtl,rtr;
        ll cnt = 0;
        for (int i=r;i>=1;i-=i&-i){
            rtr.push_back(root[i]);
            cnt += seg[root[i]].cnt;
        } 
        for (int i=l-1;i>=1;i-=i&-i){
            rtl.push_back(root[i]);
            cnt -= seg[root[i]].cnt;
        }

        if (cnt<k) return -1;
        return queryk(rtl,rtr,1,m,k);
    }
};

const int INF = 2147483647;

void solve(){
    int n,q;
    cin >> n >> q;

    vector<int> a(n+1);
    for (int i=1;i<=n;i++){
        cin >> a[i];
    }

    auto uni = a;
    vector<array<int,5>> que(q+1);
    for (int i=1;i<=q;i++){
        int op,l,r,pos,k;
        cin >> op;
        if (op==3){
            cin >> pos >> k;
        }
        else{
            cin >> l >> r >> k;
        }
        if (op!=2){
            uni.push_back(k);
        }
        que[i] = {op,l,r,pos,k};
    }

    sort(uni.begin()+1,uni.end());
    uni.erase(unique(uni.begin()+1,uni.end()),uni.end());
    int m = uni.size()-1;

    for (int i=1;i<=n;i++){
        a[i] = lower_bound(uni.begin()+1,uni.end(),a[i])-uni.begin();
    }
    for (int t=1;t<=q;t++){
        auto& [op,l,r,pos,k] = que[t];
        if (op!=2){
            k = lower_bound(uni.begin()+1,uni.end(),k)-uni.begin();
        }
    }

    FenwickSegmentTree fsg(n,m);
    fsg.build(a);

    for (int t=1;t<=q;t++){
        auto& [op,l,r,pos,k] = que[t];
        if (op==1){
            cout << fsg.query_range(l,r,1,k-1)+1 << '\n';
        }
        else if (op==2){
            cout << uni[fsg.queryk(l,r,k)] << '\n';
        }
        else if (op==3){
            fsg.change(pos,a[pos],k);
            a[pos] = k;
        }
        else if (op==4){
            ll cnt = fsg.query_range(l,r,1,k-1);
            int res = fsg.queryk(l,r,cnt);
            if (res==-1){
                cout << -INF << '\n';
            }
            else{
                cout << uni[res] << '\n';
            }
        }
        else{
            ll cnt = fsg.query_range(l,r,1,k);
            int res = fsg.queryk(l,r,cnt+1);
            if (res==-1){
                cout << INF << '\n';
            }
            else{
                cout << uni[res] << '\n';
            }
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int t = 1;
    // cin >> t;
    while (t--) solve();

    return 0;
}
posted @ 2026-07-29 11:48  kzssCCC  阅读(12)  评论(0)    收藏  举报