树套树 ---- 树状数组套权值线段树模板题 P2617 Dynamic Rankings 动态第K大

题目链接


题目大意:

给你一个数组 a a a, a a a有两个操作

  1. 询问 a a a [ l , r ] [l,r] [l,r]区间里面第 k k k小的数是哪个?
  2. 修改 a x a_x ax y y y

解题思路:

首先我们知道权值线段树是可以求第 k k k小的数的,如果只有第一个问题那么就是一个主席树问题,但是主席树对于第二个问题,要修改 n n n棵树时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)的很大不行

我们知道主席树是怎么建立的?
r o o t [ i ] 继 承 了 r o o t [ i − 1 ] 的 到 的 连 续 区 间 的 权 值 线 段 树 root[i]继承了root[i-1]的到的连续区间的权值线段树 root[i]root[i1]线
因为你修改一个点你要把影响传到后面的所有的 r o o t root root,那么开销很大
那么这个是不是很想树状数组??

树状数组修改的时候只把影响传给了后面的 l o g log log个节点,每次查询都要去询问 l o g log log个节点

在这里插入图片描述
那么我们的思路就来了,就是树状数组每个节点都是一个动态开点的权值线段树,现在我们要更新 a 5 a_5 a5,那么我们只有更新 r o o t [ 5 , 6 , 8 , 16 ] root[5,6,8,16] root[5,6,8,16]这4个节点的权值线段树

其实我们发现树状数组的作用是分层,把序列分成了 l o g log log层,每次这个影响只会传到每一层的一个点去

到时候查询的时候要 l o g log log个节点一起跳这样才能完整的统计影响


AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
struct qu {
    char op[2];
    int l, r, k;
}q[maxn];
struct node {
    int lson, rson;
    int val;
}sgt[maxn * 40];
int n, root[maxn], cnt, m;
int arr[maxn];
vector<int> lis;
inline int getid(int x) {
    return lower_bound(lis.begin(),lis.end(),x) - lis.begin() + 1;
}

inline void pushup(int rt) {
    sgt[rt].val = sgt[sgt[rt].lson].val + sgt[sgt[rt].rson].val;
    return;
}

inline void insert(int &rt, int l, int r, int pos, int val = 1) {
    if(!rt) rt = ++ cnt;
    if(l == r) {
        sgt[rt].val += val;
        return;
    }
    if(pos <= mid) insert(sgt[rt].lson,l,mid,pos,val);
    else insert(sgt[rt].rson,mid+1,r,pos,val);
    pushup(rt);
}

int jump[2][30], idx[2];
inline void pre_quary(int l, int r) {
    idx[0] = idx[1] = 0;
    while(l) {
        jump[0][++idx[0]] = root[l];
        l -= lowbit(l);
    }
    
    while(r) {
        jump[1][++idx[1]] = root[r];
        r -= lowbit(r);
    }
}

int quary(int l, int r, int kth) {
    if(l == r) return l;
    int lsiz = 0, rsiz = 0;//log个节点一起条
    for(int i = 1; i <= idx[0]; ++ i)  lsiz += sgt[sgt[jump[0][i]].lson].val;
    for(int i = 1; i <= idx[1]; ++ i)  rsiz += sgt[sgt[jump[1][i]].lson].val;
    
    if(kth <= rsiz - lsiz) {
        for(int i = 1; i <= idx[0]; ++ i)  jump[0][i] = sgt[jump[0][i]].lson;
        for(int i = 1; i <= idx[1]; ++ i)  jump[1][i] = sgt[jump[1][i]].lson;
        return quary(l,mid,kth);        
    } else {
        for(int i = 1; i <= idx[0]; ++ i)  jump[0][i] = sgt[jump[0][i]].rson;
        for(int i = 1; i <= idx[1]; ++ i)  jump[1][i] = sgt[jump[1][i]].rson;
        return quary(mid+1,r,kth-(rsiz-lsiz));        
    }
}

int main() {
    IOS;
    cin >> n >> m;
    for(int i = 1; i <= n; ++ i) {
        cin >> arr[i];
        lis.push_back(arr[i]);
    } 
    
    for(int i = 1; i <= m; ++ i){
        cin >> q[i].op;
        if(*q[i].op == 'C') cin >> q[i].l >> q[i].r, lis.push_back(q[i].r);
        else cin >> q[i].l >> q[i].r >> q[i].k;
    }
    
    sort(lis.begin(),lis.end());
    lis.erase(unique(lis.begin(),lis.end()),lis.end());
    int len = lis.size() + 1;

    for(int i = 1; i <= n; ++ i) {
        arr[i] = getid(arr[i]);//像树状数组一样跟新
        int j = i;
        while(j < len) {
            insert(root[j],1,len,arr[i]);
            j += lowbit(j);
        }
    }
    
    for(int i = 1; i <= m; ++ i) {
        char op = *q[i].op;
        if(op == 'C') {
            int ax = q[i].l, y = q[i].r;
            int j = ax;
            while(j < len) {
                insert(root[j],1,len,arr[q[i].l],-1);
                j += lowbit(j);
            }  
            j = ax;
            arr[q[i].l] = getid(q[i].r);
            while(j < len) {
                insert(root[j],1,len,arr[q[i].l],1);
                j += lowbit(j);
            }  
        } else {
            pre_quary(q[i].l-1,q[i].r);
            cout << lis[quary(1,len,q[i].k) - 1]  << "\n";
        }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值