P2617 Dynamic Rankings
显然单单写一个主席树是不够的,主席树可以维护的是静态区间第k大。本题要求动态修改,所以考虑带修改的主席树怎么写。 首先很显然一个小thick是,我们把修改的部分和原本静态的分开,单独形成新的主席树。这样的好处有很多:处理方便,思路清晰,更好理解题目。 如果暴力修改的话,每次修改一个点,我们最多需要修改n棵线段树。所以复杂度太高,无法接受。其实带修改的主席树,就是树套树嘛,主席树套了一个树状数组,把每棵线段树当成一个点,你就好理解了。和树状数组是一样的,本来我们维护一个前缀和,可以O(1)查询区间和。但是修改是O(n)的。用了树状数组以后,我们修改是log的,查询也是log的。同理,套了树状数组以后,我们每次修改只需要修改log棵线段树,查询也是。所以总的复杂度是
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O ( n l o g 2 n ) 。 关于代码实现: (1)因为中间修改的时候会新建立节点,所以空间开的时候乘50或者再大一点,否则RE。 (2)为了方便查询,我们有一个s数组。s[i]表示在第i个节点被修改过之后所在的节点。 (3)查询的时候,为了方便,我们提前把需要计算到的节点编号存在数组里面。注意好想象的“点”与线段树的联系。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N= 2e5 + 10 ;
int n, m, tot, sz, num, a[ N] , b[ N] , rt[ N] , s[ N] , sum[ N* 300 ] , ls[ N* 300 ] , rs[ N* 300 ] , rootl[ 50 ] , rootr[ 50 ] , cntl, cntr;
struct node{
int l, r, k, op;
} q[ N] ;
int get ( int x) { return lower_bound ( b+ 1 , b+ sz+ 1 , x) - b; }
int lowbit ( int x) { return x& ( - x) ; }
struct Tree{
void update ( int & o, int pre, int l, int r, int x) {
o= ++ tot;
sum[ o] = sum[ pre] + 1 ;
ls[ o] = ls[ pre] ;
rs[ o] = rs[ pre] ;
if ( l== r) return ;
int mid= l+ r>> 1 ;
if ( x<= mid) update ( ls[ o] , ls[ pre] , l, mid, x) ;
else update ( rs[ o] , rs[ pre] , mid+ 1 , r, x) ;
}
void add_update ( int & o, int l, int r, int x, int val) {
if ( o== 0 ) o= ++ tot;
sum[ o] + = val;
if ( l== r) return ;
int mid= l+ r>> 1 ;
if ( x<= mid) add_update ( ls[ o] , l, mid, x, val) ;
else add_update ( rs[ o] , mid+ 1 , r, x, val) ;
}
void add_update ( int k, int val) {
int x= get ( a[ k] ) ; int y= get ( val) ;
a[ k] = val;
while ( k<= n) {
add_update ( s[ k] , 1 , sz, x, - 1 ) ;
add_update ( s[ k] , 1 , sz, y, 1 ) ;
k+ = lowbit ( k) ;
}
}
int query ( int last, int now, int l, int r, int x) {
if ( l== r) return l;
int cnt= sum[ ls[ now] ] - sum[ ls[ last] ] ;
for ( int i= 1 ; i<= cntl; ++ i) cnt- = sum[ ls[ rootl[ i] ] ] ;
for ( int i= 1 ; i<= cntr; ++ i) cnt+ = sum[ ls[ rootr[ i] ] ] ;
int mid= l+ r>> 1 ;
if ( cnt>= x) {
for ( int i= 1 ; i<= cntl; ++ i) rootl[ i] = ls[ rootl[ i] ] ;
for ( int i= 1 ; i<= cntr; ++ i) rootr[ i] = ls[ rootr[ i] ] ;
return query ( ls[ last] , ls[ now] , l, mid, x) ;
} else {
for ( int i= 1 ; i<= cntl; ++ i) rootl[ i] = rs[ rootl[ i] ] ;
for ( int i= 1 ; i<= cntr; ++ i) rootr[ i] = rs[ rootr[ i] ] ;
return query ( rs[ last] , rs[ now] , mid+ 1 , r, x- cnt) ;
}
}
int kth ( int l, int r, int k) {
cntl= cntr= 0 ;
for ( int i= l- 1 ; i; i- = lowbit ( i) ) rootl[ ++ cntl] = s[ i] ;
for ( int i= r; i; i- = lowbit ( i) ) rootr[ ++ cntr] = s[ i] ;
int ans= query ( rt[ l- 1 ] , rt[ r] , 1 , sz, k) ;
return b[ ans] ;
}
} tr;
int main ( ) {
scanf ( "%d%d" , & n, & m) ;
for ( int i= 1 ; i<= n; ++ i) scanf ( "%d" , & a[ i] ) , b[ ++ sz] = a[ i] ;
for ( int i= 1 ; i<= m; ++ i) {
char ch; scanf ( " %c" , & ch) ;
if ( ch== 'Q' ) { q[ i] . op= 1 ;
scanf ( "%d%d%d" , & q[ i] . l, & q[ i] . r, & q[ i] . k) ;
} else { q[ i] . op= 2 ;
scanf ( "%d%d" , & q[ i] . l, & q[ i] . r) ; b[ ++ sz] = q[ i] . r;
}
}
sort ( b+ 1 , b+ sz+ 1 ) ;
int num= unique ( b+ 1 , b+ sz+ 1 ) - b- 1 ;
sz= num;
for ( int i= 1 ; i<= n; ++ i) tr. update ( rt[ i] , rt[ i- 1 ] , 1 , sz, get ( a[ i] ) ) ;
for ( int i= 1 ; i<= m; ++ i) {
if ( q[ i] . op== 1 ) {
printf ( "%d\n" , tr. kth ( q[ i] . l, q[ i] . r, q[ i] . k) ) ;
} else {
tr. add_update ( q[ i] . l, q[ i] . r) ;
}
}
return 0 ;
}