树状数组套主席树
前言
静态查询区间第 k 小 :主席树即可动态查询区间第 k 小 :即边单点修改边查询区间第 k 小 。主席树查询区间第 k 小是基于前缀思想的,而树状数组可以很好的解决单点修改的前缀查询的问题。如何将这两者一起套用就是这个部分要学的。
算法学习
单点修改: 让树状数组的
l
o
g
n
logn
l o g n 棵主席树单点修改,时间复杂度:
O
(
l
o
g
2
n
)
O(log^2n)
O ( l o g 2 n ) 区间查询第 k 大: 区间
[
l
,
r
]
[l,r]
[ l , r ] 是由树状数组中的
l
o
g
n
logn
l o g n 棵主席树做差相减构成的。先记录这
l
o
g
n
logn
l o g n 根主席树的根,在二分求第 k 小时并行更新这
l
o
g
n
logn
l o g n 棵主席树的区间的根即可。写法: 不继承,直接动态新开点,用到多少点开多少点。
模板代码
例题链接
# include <bits/stdc++.h>
using namespace std;
const int N= 1e5 + 10 ;
struct T {
int v, l, r;
} tr[ N* 100 ] ;
struct operation {
char op;
int l, r, x, k;
} q[ N] ;
char op;
int n, m, cnt, root[ N] , q1[ N] , q2[ N] , siz1, siz2, a[ N] , b[ N* 10 ] , R;
void update ( int & now, int l, int r, int x, int k) {
if ( ! now) now= ++ cnt;
if ( l== r) {
tr[ now] . v+= k;
return ;
}
int mid= ( l+ r) / 2 ;
if ( x<= mid) update ( tr[ now] . l, l, mid, x, k) ;
else update ( tr[ now] . r, mid+ 1 , r, x, k) ;
tr[ now] . v= tr[ tr[ now] . l] . v+ tr[ tr[ now] . r] . v;
}
int lowbit ( int x) {
return x& ( - x) ;
}
void prepare_update ( int x, int k) {
int tmp= lower_bound ( b+ 1 , b+ R+ 1 , a[ x] ) - b;
for ( int i= x; i<= n; i+= lowbit ( i) ) update ( root[ i] , 1 , R, tmp, k) ;
}
int query ( int l, int r, int k) {
if ( l== r) return l;
int mid= ( l+ r) / 2 , sum= 0 ;
for ( int i= 1 ; i<= siz1; i++ ) sum+= tr[ tr[ q1[ i] ] . l] . v; ;
for ( int i= 1 ; i<= siz2; i++ ) sum-= tr[ tr[ q2[ i] ] . l] . v;
if ( k<= sum) {
for ( int i= 1 ; i<= siz1; i++ ) q1[ i] = tr[ q1[ i] ] . l;
for ( int i= 1 ; i<= siz2; i++ ) q2[ i] = tr[ q2[ i] ] . l;
return query ( l, mid, k) ;
} else {
for ( int i= 1 ; i<= siz1; i++ ) q1[ i] = tr[ q1[ i] ] . r;
for ( int i= 1 ; i<= siz2; i++ ) q2[ i] = tr[ q2[ i] ] . r;
return query ( mid+ 1 , r, k- sum) ;
}
}
int prepare_query ( int ql, int qr, int k) {
siz1= siz2= 0 ;
for ( int i= qr; i> 0 ; i-= lowbit ( i) ) q1[ ++ siz1] = root[ i] ;
for ( int i= ql- 1 ; i> 0 ; i-= lowbit ( i) ) q2[ ++ siz2] = root[ i] ;
return query ( 1 , R, k) ;
}
int main ( ) {
cin>> n>> m;
for ( int i= 1 ; i<= n; i++ ) {
cin>> a[ i] ;
b[ ++ R] = a[ i] ;
}
for ( int i= 1 ; i<= m; i++ ) {
cin>> q[ i] . op;
if ( q[ i] . op== 'Q' ) cin>> q[ i] . l>> q[ i] . r>> q[ i] . k;
else {
cin>> q[ i] . x>> q[ i] . k;
b[ ++ R] = q[ i] . k;
}
}
sort ( b+ 1 , b+ R+ 1 ) ;
R= unique ( b+ 1 , b+ R+ 1 ) - b- 1 ;
for ( int i= 1 ; i<= n; i++ ) prepare_update ( i, 1 ) ;
for ( int i= 1 ; i<= m; i++ ) {
if ( q[ i] . op== 'Q' ) cout<< b[ prepare_query ( q[ i] . l, q[ i] . r, q[ i] . k) ] << endl;
else {
prepare_update ( q[ i] . x, - 1 ) ;
a[ q[ i] . x] = q[ i] . k;
prepare_update ( q[ i] . x, 1 ) ;
}
}
return 0 ;
}