嘿嘿,题目只是一个幌子,我还是肝主席树233。 但是,为什么会有这样一个名字?考虑假如不管空间,我们有一个最暴力的写法。开一个
m
∗
n
m*n
m ∗ n 的数组,每次修改某一时刻某一位置的值,我们直接把它修改掉就好。然后把它当成新的时刻,所以需要
O
(
n
)
O(n)
O ( n ) 的赋值。这个算法无论是空间还是时间,复杂度我们都无法接受。有了之前可持久化的思路,我们考虑能不能有一种可持久的数组,把空间和时间都降低呢? 单独对一个区间来说,线段树的空间复杂度和时间复杂度比数组还高,但是为什么我们选了它,因为有主席树啊。虽然
m
m
m 个时刻,我们相当于开了
m
m
m 棵线段树,但我们的是可持久的,每次只会多
l
o
g
n
log n
l o g n 个节点。这样,时空均匀且降低。 这次套用的就是普通线段树了,权值线段树在本题无用。还有一个注意点,就算当前点不修改,也要承继一下它所给的历史版本。(题目描述我佛了)。
#include <bits/stdc++.h>
using namespace std;
const int N= 1e6 + 10 ;
int n, m, tot, a[ N] , rt[ N] , val[ N* 20 ] , ls[ N* 20 ] , rs[ N* 20 ] ;
int read ( ) {
char ch= getchar ( ) ; int num= 0 , f= 1 ;
while ( ! isdigit ( ch) ) { if ( ch== '-' ) f= - 1 ; ch= getchar ( ) ; }
while ( isdigit ( ch) ) { num= ( num<< 1 ) + ( num<< 3 ) + ( ch^ 48 ) ; ch= getchar ( ) ; }
return num* f;
}
void build ( int & o, int l, int r) {
o= ++ tot;
if ( l== r) {
val[ o] = a[ l] ; return ;
}
int mid= l+ r>> 1 ;
build ( ls[ o] , l, mid) ;
build ( rs[ o] , mid+ 1 , r) ;
}
void update ( int & o, int pre, int l, int r, int x, int va) {
o= ++ tot;
ls[ o] = ls[ pre] ; rs[ o] = rs[ pre] ;
if ( l== r) {
val[ o] = va; return ;
}
int mid= l+ r>> 1 ;
if ( x<= mid) update ( ls[ o] , ls[ pre] , l, mid, x, va) ;
else update ( rs[ o] , rs[ pre] , mid+ 1 , r, x, va) ;
}
int query ( int o, int l, int r, int x) {
if ( l== r) return val[ o] ;
int mid= l+ r>> 1 ;
if ( x<= mid) return query ( ls[ o] , l, mid, x) ;
else return query ( rs[ o] , mid+ 1 , r, x) ;
}
int main ( ) {
n= read ( ) , m= read ( ) ;
for ( int i= 1 ; i<= n; ++ i) a[ i] = read ( ) ;
build ( rt[ 0 ] , 1 , n) ;
int v, op, x, y;
for ( int i= 1 ; i<= m; ++ i) {
v= read ( ) , op= read ( ) , x= read ( ) ;
if ( op== 1 ) {
y= read ( ) ; update ( rt[ i] , rt[ v] , 1 , n, x, y) ;
} else {
printf ( "%d\n" , query ( rt[ v] , 1 , n, x) ) ; rt[ i] = rt[ v] ;
}
}
return 0 ;
}