P7446 [Ynoi2007] rfplca

因为是 Ynoi 所以 我们考虑分块来维护 \(a_i\)

对于每个位置维护两个值 \(fa_i\)\(top_i\) ,分别表示它的真实祖先和块外的第一个祖先。

对于每次修改

  • 散块直接暴力修改,暴力重构块内的 \(top_i\)

  • 整块整体打标记 \(sub\) , 表示这个块被减了多少。
    注意到一个块修改 \(\sqrt n\) 次后所有 \(top_i = fa_i\) ,此时就不需要重构了(当然标记还是正常打)
    一共有 \(\sqrt n\) 个块 ,每个块重构 \(\sqrt n\) 次,每次重构复杂度为 \(\sqrt n\) ,总复杂度便为 \(n \sqrt n\)

询问像树剖一样跳就可以了。

复杂度 \(\mathcal O((n+q)\sqrt n)\)

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define LL long long

const int MAXN = 4e5 , MAXS = 650;
int n , m , q , fa[ MAXN + 5 ] , bel[ MAXN + 5 ] , l[ MAXS + 5 ] , r[ MAXS + 5 ];

int top[ MAXN + 5 ] , cnt[ MAXS + 5 ]; LL sub[ MAXS + 5 ];
void ReBuild( int id ) {
	for( int i = l[ id ] ; i <= r[ id ] ; i ++ ) fa[ i ] = max( fa[ i ] - sub[ id ] , 1ll ); sub[ id ] = 0;
	for( int i = l[ id ] ; i <= r[ id ] ; i ++ ) {
		if( fa[ i ] < l[ id ] ) top[ i ] = fa[ i ];
		else top[ i ] = top[ fa[ i ] ];
	}
}
void Update( int l , int r , int val ) {
	int ql = bel[ l ] , qr = bel[ r ];
	if( ql == qr ) {
		for( int i = l ; i <= r ; i ++ ) fa[ i ] = max( fa[ i ] - val , 1 );
		ReBuild( ql ); return;
	}
	for( int i = ql + 1 ; i <= qr - 1 ; i ++ ) {
		sub[ i ] += val; cnt[ i ] ++;
		if( cnt[ i ] <= MAXS ) ReBuild( i );
	}
	for( ; bel[ l ] == ql ; l ++ ) fa[ l ] = max( 1 , fa[ l ] - val );
	for( ; bel[ r ] == qr ; r -- ) fa[ r ] = max( 1 , fa[ r ] - val );
	ReBuild( ql ); ReBuild( qr );
}
#define BS( u ) ( max(  fa[ u ] - sub[ bel[ u ] ] , 1ll ) )
#define GS( u ) ( max( top[ u ] - sub[ bel[ u ] ] , 1ll ) )
int lca( int u , int v ) {
	for( ; bel[ u ] != bel[ v ] ; u = GS( u ) ) if( bel[ u ] < bel[ v ] ) swap( u , v );
	for( ; GS( u ) != GS( v ) ; u = GS( u ) ) if( u < v ) swap( u , v );
	for( ; u != v ; u = BS( u ) ) if( u < v ) swap( u , v );
	return u;
}  

int main( ) {
	scanf("%d %d",&n,&q); m = ( n - 1 ) / MAXS + 1;
	for( int i = 2 ; i <= n ; i ++ ) scanf("%d",&fa[ i ]);
	for( int i = 1 ; i <= n ; i ++ ) bel[ i ] = ( i - 1 ) / MAXS + 1 , r[ bel[ i ] ] = i;
	for( int i = 1 ; i <= m ; i ++ ) l[ i ] = r[ i - 1 ] + 1;
	for( int i = 1 ; i <= m ; i ++ ) ReBuild( i );
	
	for( int i = 1 , opt , l , r , x , lst = 0 ; i <= q ; i ++ ) {
		scanf("%d %d %d",&opt,&l,&r); l ^= lst , r ^= lst;
		if( opt == 1 ) scanf("%d",&x) , Update( l , r , x ^ lst );
		if( opt == 2 ) printf("%d\n", lst = lca( l , r ) );
	}
	return 0;
} 
posted @ 2021-10-22 21:41  chihik  阅读(25)  评论(0编辑  收藏  举报