[luogu P2617] Dynamic Rankings
题目大意:
带修区间第K小
题解:
这是道很模板的题目,基本思路就是树状数组套主席树。
首先对于主席树来讲是只支持静态区间第k小的,同样基于做差的思想,用树状数组维护动态开点的权值线段树,然后就行了。
看代码容易懂
Code:
#include<bits/stdc++.h>
#define N 200005
#define lowbit(x) (x & -x)
using namespace std;
int root[N], size[N * 600], ch[N * 600][2], tot, sz;
void add(int &rt, int l, int r, int x, int o){ //主席树建树
if(!rt) rt = ++ sz;// 动态开点
size[rt] += o;//因为不用保存历史版本,所以直接改
if(l == r) return;
int mid = (l + r) >> 1;
if(x <= mid) add(ch[rt][0], l, mid, x, o);
else add(ch[rt][1], mid + 1, r, x, o);
}
int a[N], b[N], sx, sy, xx[N], yy[N], n;
void update(int x, int y){//树状数组维护前缀和,每个点表示的是 i - lowbit(i) + 1 ~ i的数 组成的权值线段树
int k = lower_bound(b + 1, b + 1 + tot, a[x]) - b;
for(; x <= n; x += lowbit(x)) add(root[x], 1, tot, k, y);
}
int query(int l, int r, int x){//询问,就是权值线段树上二分(多颗)
if(l == r) return l;
int sum = 0, mid = (l + r) >> 1;
for(int i = 1; i <= sx; i ++) sum -= size[ch[xx[i]][0]];
for(int i = 1; i <= sy; i ++) sum += size[ch[yy[i]][0]];
if(x <= sum){
for(int i = 1; i <= sx; i ++) xx[i] = ch[xx[i]][0];
for(int i = 1; i <= sy; i ++) yy[i] = ch[yy[i]][0];
return query(l, mid, x);
}else{
for(int i = 1; i <= sx; i ++) xx[i] = ch[xx[i]][1];
for(int i = 1; i <= sy; i ++) yy[i] = ch[yy[i]][1];
return query(mid + 1, r, x - sum);
}
}
char c[N];
int m, l[N], r[N], k[N];
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++){
scanf("%d", &a[i]);
b[++ tot] = a[i];
}
for(int i = 1; i <= m; i ++){
scanf(" %c", &c[i]);
if(c[i] == 'Q') scanf("%d%d%d", &l[i], &r[i], &k[i]);
else scanf("%d%d", &l[i], &r[i]), b[++ tot] = r[i];
}
sort(b + 1, b + 1 + tot);
tot = unique(b + 1, b + 1 + tot) - b - 1;//xjb离散化
for(int i = 1; i <= n; i ++) update(i, 1);
for(int i = 1; i <= m; i ++){
if(c[i] == 'C'){
update(l[i], -1);
a[l[i]] = r[i];
update(l[i], 1);
}else{
sx = sy = 0;
for(int j = l[i] - 1; j; j -= lowbit(j)) xx[++ sx] = root[j];
for(int j = r[i]; j; j -= lowbit(j)) yy[++ sy] = root[j]; // 先记下需要用的每颗权值线段树的根,方便树上二分。
printf("%d\n", b[query(1, tot, k[i])]);//输出
}
}
return 0;
}
.......