Dynamic Rankings—带单点修改的主席树

这道题应该是很不错的板子了\(\mathcal{\color{cyan}{Link}}\)

\(\mathcal{\color{red}{Description}}\)

给定一个序列,有两种操作。一是要查询区间第\(k\)大,二是要支持单点修改某个元素的值。

\(\mathcal{\color{red}{Solution}}\)

我们考虑……对于支持修改这种操作……好像对于前缀和支持的函数式线段树很不友好……那我们考虑有没有什么别的可以维护前缀的东西来处理插入操作——嗯,树状数组就闪耀登场了!这玩意儿虽然对于查询好像是\(logn\)的,比朴素的前缀查询多了一个\(log\)……也就是\(O(nlog^2n)\)的时间复杂度。\(emmmm\)好像有种方式可以少一个\(log\)。以后等我什么时候会了再补上吧!

而这里我们需要注意的是,我们需要事先把所有的修改后的值离线下来,也就是说我们要把所有的修改操作离线到我们需要离散化的建树数组里。原因嘛……是因为我们的主席树是权值线段树,每个节点上的值是这个权值的出现次数。所以如果你一开始不给它留个空位置的话,它是无法被插入的。

    for(i = 1; i <= N; i ++)
        base[i] = aft[++ tot]= qr() ;
    for(i = 1; i <= M; i ++){
        cin >> mark ; 
        if(mark == 'Q') q[i].a = qr(), q[i].b = qr(), q[i].c = qr() ;
        else q[i].x = qr(), q[i].y = aft[++ tot] =qr() ;
    }
    sort(aft + 1, aft + tot + 1 ) ;
    Len = unique(aft + 1, aft + tot + 1) - aft - 1 ;

嗯,也就是说一个在线的算法又被离线了虽然对此并没有什么办法只能统统二十块

而换用树状数组之后,我们的函数式线段树的递推式子就不再是\(T_i = T_{i - 1} + f(T_i)\)了,而是$$T_i = \sum\limits_{bit(i_j) = 1}^{}{T_j}$$其中\(j\)表示\(i\)的二进制位的第\(j\)位。

而由于我们是顺序递推的,所以我们在\(insert\)时,静态中的\(last_i\)要是\(T_i\)而不是\(T_{i - 1}\),也就是说每次我们要把所有包括\(j\)的、之后待修改的状态更新\(+1\)
,大概就是这种感觉:

il int insert(int &rt, int last, int l, int r, int pos, int v){
    rt = ++ cnt ;
    sum[rt] = sum[last] + v ;
    L[rt] = L[last] ;
    R[rt] = R[last] ;
    if (l < r){
        if(pos <= mid) L[rt] = insert(L[rt], L[last], l, mid, pos, v) ;
        else R[rt] = insert(R[rt], R[last], mid + 1, r, pos, v) ;
    }
    return rt ;
}
il void add(int x, int v){
    pos = lower_bound(aft + 1, aft + Len + 1, base[x]) - aft ;
    for(j = x; j <= N; j += lowbit(j)){
        T[j] = insert(T[j], T[j], 1, Len, pos, v) ;
    }
}

那个那个\(insert\)……就是就是静态主席树中的\(insert\)

之后就到了查询的操作,查询的时候……这个这个由于牵扯到多个树的求和,所以我们每次递归要进行两次的\(logn\)的操作。

    for(i = 1; i <= M; i ++){
        if (q[i].c){
            totl = totr = 0 ;
            for(j = q[i].a - 1; j ; j -= lowbit(j)) 
                LL[++ totl] = T[j] ;
            for(j = q[i].b; j ; j -= lowbit(j)) 
                RR[++ totr] = T[j] ;
            printf("%d\n", aft[query(1, Len, q[i].c)]) ;
        }
        else{
            add(q[i].x, -1) ;
            base[q[i].x] = q[i].y ;
            add(q[i].x, 1) ;
        }   
    }
 - - - - - - - - - - - - - - - - - - - -
 int query(int l, int r, int q){
    if(l == r) return l ;
    int chk = 0 ;
    for(int k = 1; k <= totl; k ++) chk -= sum[L[LL[k]]] ;
    for(int k = 1; k <= totr; k ++) chk += sum[L[RR[k]]] ;
    if(q <= chk){
        for(int k = 1; k <= totl; k ++) LL[k] = L[LL[k]] ;
        for(int k = 1; k <= totr; k ++) RR[k] = L[RR[k]] ;
        return query(l, mid, q) ;
    }
    else {
        for(int k = 1; k <= totl; k ++) LL[k] = R[LL[k]] ;
        for(int k = 1; k <= totr; k ++) RR[k] = R[RR[k]] ;  
        return query(mid + 1, r, q - chk) ;
    }
}

嗯,我们每次都需要去求前几个树的和,所以……就没了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#define il inline
#define MAXN 200001
#define mid ((l + r) >> 1)

using namespace std ;
char mark ;
int tot, pos, cnt, aft[MAXN];
int Len, L[MAXN << 6], R[MAXN << 6], T[MAXN << 6], sum[MAXN << 6];
int LL[MAXN], RR[MAXN] ;
int N, M, base[MAXN], i, j, totl, totr;
struct node{
    int a, b, c, x, y ;
}q[MAXN];

il int lowbit(int x){
    return x & (-x) ;
}
il int qr(){
    int k = 0, f = 1; 
    char c = getchar() ;
    while(!isdigit(c)){
        if (c == '-') f = -1 ;
        c = getchar() ;
    }
    while(isdigit(c)){
        k = (k << 1) + (k << 3) + c - 48 ;
        c = getchar() ;
    } 
    return k * f;
}
il int insert(int &rt, int last, int l, int r, int pos, int v){
    rt = ++ cnt ;
    sum[rt] = sum[last] + v ;
    L[rt] = L[last] ;
    R[rt] = R[last] ;
    if (l < r){
        if(pos <= mid) L[rt] = insert(L[rt], L[last], l, mid, pos, v) ;
        else R[rt] = insert(R[rt], R[last], mid + 1, r, pos, v) ;
    }
    return rt ;
}
il void add(int x, int v){
    pos = lower_bound(aft + 1, aft + Len + 1, base[x]) - aft ;
    for(j = x; j <= N; j += lowbit(j)){
        T[j] = insert(T[j], T[j], 1, Len, pos, v) ;
    }
}
int query(int l, int r, int q){
    if(l == r) return l ;
    int chk = 0 ;
    for(int k = 1; k <= totl; k ++) chk -= sum[L[LL[k]]] ;
    for(int k = 1; k <= totr; k ++) chk += sum[L[RR[k]]] ;
    if(q <= chk){
        for(int k = 1; k <= totl; k ++) LL[k] = L[LL[k]] ;
        for(int k = 1; k <= totr; k ++) RR[k] = L[RR[k]] ;
        return query(l, mid, q) ;
    }
    else {
        for(int k = 1; k <= totl; k ++) LL[k] = R[LL[k]] ;
        for(int k = 1; k <= totr; k ++) RR[k] = R[RR[k]] ;  
        return query(mid + 1, r, q - chk) ;
    }
}
int main(){
    cin >> N >> M ;
    for(i = 1; i <= N; i ++)
        base[i] = aft[++ tot]= qr() ;
    for(i = 1; i <= M; i ++){
        cin >> mark ; 
        if(mark == 'Q') q[i].a = qr(), q[i].b = qr(), q[i].c = qr() ;
        else q[i].x = qr(), q[i].y = aft[++ tot] =qr() ;
    }
    sort(aft + 1, aft + tot + 1 ) ;
    Len = unique(aft + 1, aft + tot + 1) - aft - 1 ;
    for(i = 1; i <= N; i ++) add(i, 1) ;
    for(i = 1; i <= M; i ++){
        if (q[i].c){
            totl = totr = 0 ;
            for(j = q[i].a - 1; j ; j -= lowbit(j)) 
                LL[++ totl] = T[j] ;
            for(j = q[i].b; j ; j -= lowbit(j)) 
                RR[++ totr] = T[j] ;
            printf("%d\n", aft[query(1, Len, q[i].c)]) ;
        }
        else{
            add(q[i].x, -1) ;
            base[q[i].x] = q[i].y ;
            add(q[i].x, 1) ;
        }   
    }
} 
posted @ 2018-06-27 21:48  皎月半洒花  阅读(468)  评论(0编辑  收藏  举报