P2617 Dynamic Rankings[带修主席树(模板)]

D y n a m i c   R a n k i n g s Dynamic\ Rankings Dynamic Rankings

给出一个长度为 N N N 的数列 A i A_i Ai, 要求支持

  • 区间查询第 K K K 大 .
  • 单点修改 .

N ≤ 1 0 5 N \le 10^5 N105


普通主席树是使用每个位置 (设为 i i i) 重建一颗 权值线段树, 每颗线段树包含 [ 1 , i ] [1, i] [1,i] 的信息, 再用动态开点的方法充分利用线段树间重复信息, 以 O ( N l o g N ) O(NlogN) O(NlogN) 的时空复杂度, 将 前缀和 操作应用到 权值线段树 之间, 实现 静态区间查询第 K K K 大 .


带修主席树的作用顾名思义, 支持 动态查询区间第 K K K 大 .

动态地对前缀和进行修改, 可以使用树状数组,
树状数组 每个节点保存 一个权值线段树 的根节点编号,
这里放出树状数组的图示,


每个权值线段树保存的不再是 [ 1 , i ] [1, i] [1,i] 的信息了, 保存的是什么呢?

举个例子,
图中编号为 1 , 3 , 5 , 7 1,3,5,7 1,3,5,7 的节点分别保存了 1 , 3 , 5 , 7 1,3,5,7 1,3,5,7 单点的信息,
图中编号为 2 2 2 保存的是 [ 1 , 2 ] [1, 2] [1,2] 的信息,
编号为 6 6 6 的点保存 [ 5 , 6 ] [5, 6] [5,6] 的信息
         . \ \ \ \ \ \ \ \ .         .
         . \ \ \ \ \ \ \ \ .         .
         . \ \ \ \ \ \ \ \ .         .
以此类推 .

所以此时 r o t [ i ] rot[i] rot[i] 表示的 权值线段树 保存的不再是 [ 1 , i ] [1, i] [1,i] 的信息, 而是 i i i 在树状数组中代表的区间的信息 .


注意查询时不能单独提出 r o t [ r ] rot[r] rot[r] r o t [ l − 1 ] rot[l-1] rot[l1] 进行递归查询,
因为这样向下递归调用儿子时仅能调动 r o t [ r ] rot[r] rot[r] r o t [ l − 1 ] rot[l-1] rot[l1] 的儿子, 无法代表整个 查 询 区 间 查询区间 ,

正 确 的 做 法 正确的做法 是 事先提出所有的关于 查 询 区 间 查询区间 的树状数组节点, 在往儿子节点递归的同时, O ( l o g n ) O(logn) O(logn) 将现有的 代 表 父 亲 代表父亲 的 树状数组节点 转化为 代 表 儿 子 节 点 代表儿子节点 , 保证 查 询 区 间 查询区间 的完整性 .


一共要加入 主席树 N N N 个节点, 每次加入要修改 l o g N logN logN 个树状数组节点, 每个树状数组节点需要 往下递归修改 O ( l o g N ) O(logN) O(logN) 个,
所以 时 空 复 杂 度 时空复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) .


注 意 c n t a 与 c n t b 的 清 空 . \color{red}{注意 cnt_a 与 cnt_b 的清空.} cntacntb.
注 意 离 散 数 组 B [ ] 要 开 2 倍 , 因 为 B 处 理 原 数 列 的 同 时 还 要 处 理 修 改 时 新 出 现 的 值 . \color{red}{注意离散数组B[]要开2倍,因为B处理原数列的同时还要处理修改时新出现的值.} B[]2,B.

#include<bits/stdc++.h>
#define reg register

const int maxn = 1e5 + 5;

int N;
int M;
int Len;
int cnt_a;
int cnt_b;
int A[maxn];
int B[maxn<<1];
int A1[maxn];
int B1[maxn];
int rot[maxn];

namespace President_Tree{

        struct Node{ int sum, lt, rt; } T[maxn*400]; 
        int node_cnt = 0;

        void Add(int &k, int l, int r, int aim, int opt){
                if(!k) k = ++ node_cnt;
                T[k].sum += opt;
                if(l == r) return ;
                int mid = l+r >> 1;
                if(aim <= mid) Add(T[k].lt, l, mid, aim, opt);
                else Add(T[k].rt, mid+1, r, aim, opt);
        }

        int Query(int l, int r, int aim){
                if(l == r) return l;
                int sum = 0;
                for(reg int i = 1; i <= cnt_b; i ++) sum += T[T[B1[i]].lt].sum;
                for(reg int i = 1; i <= cnt_a; i ++) sum -= T[T[A1[i]].lt].sum;
                int mid = l+r >> 1;
                if(aim <= sum){
                        for(reg int i = 1; i <= cnt_b; i ++) B1[i] = T[B1[i]].lt;
                        for(reg int i = 1; i <= cnt_a; i ++) A1[i] = T[A1[i]].lt;
                        return Query(l, mid, aim);
                } 
                for(reg int i = 1; i <= cnt_b; i ++) B1[i] = T[B1[i]].rt;
                for(reg int i = 1; i <= cnt_a; i ++) A1[i] = T[A1[i]].rt;
                return Query(mid+1, r, aim-sum);
        }

}

struct Que{ int opt, l, r, k, pos, to; } Q[maxn];

int main(){
        scanf("%d%d", &N, &M);
        for(reg int i = 1; i <= N; i ++) scanf("%d", &A[i]), B[i] = A[i];
        int cnt = N;
        for(reg int i = 1; i <= M; i ++){
                char s[3];
                scanf("%s", s);
                if(s[0] == 'Q') Q[i].opt = 1, scanf("%d%d%d", &Q[i].l, &Q[i].r, &Q[i].k);
                else Q[i].opt = 0, scanf("%d%d", &Q[i].pos, &Q[i].to), B[++ cnt] = Q[i].to;
        }
        std::sort(B+1, B+cnt+1); Len = std::unique(B+1, B+cnt+1) -B-1;
        for(reg int i = 1; i <= N; i ++){
 		       	A[i] = std::lower_bound(B+1, B+Len+1, A[i]) - B;
                int t = i;
                while(t <= N) President_Tree::Add(rot[t], 1, Len, A[i], 1), t += t&-t;
    	}
    	
        for(reg int i = 1; i <= M; i ++){
                if(Q[i].opt == 1){
                	cnt_b = cnt_a = 0;
                	int t = Q[i].r;
                	while(t >= 1) B1[++ cnt_b] = rot[t], t -= t&-t;
                    t = Q[i].l - 1;
                	while(t >= 1) A1[++ cnt_a] = rot[t], t -= t&-t;
            		printf("%d\n", B[President_Tree::Query(1, Len, Q[i].k)]);
	        	}
                else{
                        int t = Q[i].pos;
                        while(t <= N) President_Tree::Add(rot[t], 1, Len, A[Q[i].pos], -1), t += t&-t;
                        A[Q[i].pos] = std::lower_bound(B+1, B+Len+1, Q[i].to) - B, t = Q[i].pos; 
                        while(t <= N) President_Tree::Add(rot[t], 1, Len, A[Q[i].pos], 1), t += t&-t;
                }
        }
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值