[FJOI2018]领导集团问题
\(link\)
这道题,是一个线段树合并优化\(dp\)的题.
我们先想朴素的\(dp\)式子
\(f [i][j]\)表示以\(i\)为根节点,最小值为\(j\)的时候最大部门子集,那么考虑对于任意一个状态,我们有两种情况:选择根节点和不选择根节点
由于这道题的状态表示,所以对于每个状态,他所选择的数一定在他的子节点内
如果不选择根节点,\(f [i][j] = \sum_{y \in \ I's \ son}f [y][j]\)
如果选择根节点,那么\(f [i][j] = f [i][c [x].rk] + 1\)
对两者取\(max\)即可
本人表述能力有限,直接看代码比较好
inline void dfs( int x ){
for( R i = head [x] ; ~i ; i = a [i].next ){
int y = a [i].to ;
dfs( y ) ;
for( R j = 1 ; j <= knt ; j ++ )
f [x][j] += f [y][j] ;
}
for( R i = 1 ; i <= c [x].rk ; i ++ )
f [x][i] = max( f [x][i] , f [x][c [x].rk] + 1 ) ;
}
我们在考虑对这个代码进行优化,很显然\(dfs\)并不能优化掉,这里面主要费时间的就是一个区间加区间和一个区间取\(max\)
这个时候我们就可以对区间加法进行标记下传,对区间去\(max\)进行标记永久化,这样实现难度和码量都会降低(因为\(max\)打永久化是真的香)
细节见代码注释
//对加法标记进行释放,一路上的([max标记的]data)的max就是最终查询的答案
#include <cstring>
#include <cstdio>
#include <algorithm>
#define R register int
#define printf tz1 = printf
using namespace std ;
const int N = 2e5 + 10 ;
typedef long long L ;
typedef double D ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch > '9' || ch < '0' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch - '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
int tz1 , n , cnt , gnt , knt , head [N] , root [N] , p ;
struct S{ int w , index , rk ; } c [N] ;
struct E{ int fr , to , next ; } a [N] ;
struct T{ int l , r , ls , rs , data , lz ; } t [N << 6] ;
inline bool cmp( S a , S b ){ return a.w < b.w ; }
inline bool csp( S a , S b ){ return a.index < b.index ; }
inline void add( int f , int t ){
a [++ cnt].fr = f ;
a [cnt].to = t ;
a [cnt].next = head [f] ;
head [f] = cnt ;
}
inline void sp( int x ){ //下传加法标记
if( ! t [x].lz ) return ;
if( t [x].ls ) t [t [x].ls].data += t [x].lz , t [t [x].ls].lz += t [x].lz ;
if( t [x].rs ) t [t [x].rs].data += t [x].lz , t [t [x].rs].lz += t [x].lz ;
t [x].lz = 0 ;
}
inline int ask( int x , int k ){ //单点查询:对加法标记进行释放,对max标记一路取max进行处理
if( !x ) return 0 ;
if( t [x].l == t [x].r ) return t [x].data ;
sp( x ) ;
int mid = ( t [x].l + t [x].r ) >> 1 ;
if( k <= mid ) return max( t [x].data , ask( t [x].ls , k ) ) ;
else return max( t [x].data , ask( t [x].rs , k ) ) ;
}
inline void cge( int &x , int Ls , int Rs , int l , int r , int k ){ //区间取max,直接标记永久化
if( !x ) x = ++ gnt , t [x].l = Ls , t [x].r = Rs ;
// printf( "CGE%d %d %d %d %d\n" , x , t [x].l , t [x].r , l , r ) ;
if( t [x].l >= l && t [x].r <= r ){ t [x].data = max( t [x].data , k ) ; return ; } // 这里实现标记,其实是将标记打在数据上,这就是给max打永久标记的精髓和优势
sp( x ) ;
int mid = ( t [x].l + t [x].r ) >> 1 ;
if( l <= mid ) cge( t [x].ls , Ls , mid , l , r , k ) ;
if( r > mid ) cge( t [x].rs , mid + 1 , Rs , l , r , k ) ;
}
inline int mer( int x , int y , int lax , int rax ){ //线段树合并实现区间加法
lax = max( lax , t [x].data ) , rax = max( rax , t [y].data ) ;
if( !x || ! y ){
if( x ) t [x].data += rax , t [x].lz += rax ; //这个格外注意,防止两个都是0对0号节点操作导致错误
if( y ) t [y].data += lax , t [y].lz += lax ;
return x + y ;
} sp ( x ) , sp ( y ) ;
t [x].ls = mer( t [x].ls , t [y].ls , lax , rax ) ;
t [x].rs = mer( t [x].rs , t [y].rs , lax , rax ) ;
t [x].data = rax + lax ; return x ;
}
inline void dfs( int x ){
// printf( "DFS%d\n" , x ) ;
for( R i = head [x] ; ~i ; i = a [i].next ){
int y = a [i].to ;
dfs( y ) ;
root [x] = root [y] = mer( root [x] , root [y] , 0 , 0 ) ;
} if( c [x].rk >= 1 )
cge( root [x] , 1 , knt , 1 , c [x].rk , ask( root [x] , c [x].rk ) + 1 ) ;
}
void sc(){
n = read() ; memset( head , -1 , sizeof( head ) ) ;
for( R i = 1 ; i <= n ; i ++ ) c [i].w = read() , c [i].index = i ;
for( R i = 2 ; i <= n ; i ++ ) p = read() , add( p , i ) ;
sort( c + 1 , c + 1 + n , cmp ) ;
c [0].w = -0x7fffffff ;
for( R i = 1 ; i <= n ; i ++ ) if( c [i].w == c [i - 1].w ) c [i].rk = knt ; else c [i].rk = ++ knt ;
sort( c + 1 , c + 1 + n , csp ) ;
}
void work(){
dfs( 1 ) ;
printf( "%d\n" , ask( root [1] , 1 ) ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
$The \ light \ has \ betrayed \ me$

浙公网安备 33010602011771号