1. 程式人生 > >[BZOJ3631]-[JLOI2014]松鼠的新家-樹上差分

[BZOJ3631]-[JLOI2014]松鼠的新家-樹上差分

說在前面

昨天看了一天置換群,感覺藥丸
於是今天來刷一刷題


題目

BZOJ3631傳送門
看題可戳傳送門


解法

就相當於是鏈加,樹上差分一下就好了
u++,v++
Lca-1,fa[Lca]-1
完了之後dfs統計一下,子樹和就是答案


下面是程式碼

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , a[300005] , tp , head[300005] , lg_ ;
struct
Path{ int pre , to ; } p[600005] ; void In( int t1 , int t2 ){ p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ; p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ; } int dep[300005] , fa[20][300005] , maxd ; void dfs_pre( int u , int f ){ for( int i = head[u] ; i ; i = p[i].pre ){ int
v = p[i].to ; if( v == f ) continue ; fa[0][v] = u , dep[v] = dep[u] + 1 ; dfs_pre( v , u ) ; } maxd = max( maxd , dep[u] ) ; } int Lca( int u , int v ){ if( dep[u] < dep[v] ) swap( u , v ) ; int t = dep[u] - dep[v] , x = 0 ; while( t ){ if( t & 1
) u = fa[x][u] ; t >>= 1 , x ++ ; } if( u == v ) return u ; for( int i = lg_ ; i >= 0 ; i -- ) if( fa[i][u] != fa[i][v] ) u = fa[i][u] , v = fa[i][v] ; return fa[0][u] ; } void preWork(){ dfs_pre( 1 , 1 ) ; for( lg_ = 1 ; ( 1 << ( lg_ + 1 ) ) <= maxd ; lg_ ++ ) ; for( int i = 1 ; i <= lg_ ; i ++ ) for( int j = 1 ; j <= N ; j ++ ) fa[i][j] = fa[i-1][ fa[i-1][j] ] ; } int tag[300005] ; void dfs( int u ){ for( int i = head[u] ; i ; i = p[i].pre ){ int v = p[i].to ; if( v == fa[0][u] ) continue ; dfs( v ) , tag[u] += tag[v] ; } } void solve(){ for( int i = 2 ; i <= N ; i ++ ){ int LCA = Lca( a[i-1] , a[i] ) ; tag[ a[i-1] ] ++ , tag[ fa[0][a[i]] ] ++ ; tag[ LCA ] -- , tag[ fa[0][LCA] ] -- ; } dfs( 1 ) ; for( int i = 1 ; i <= N ; i ++ ) printf( "%d\n" , tag[i] ) ; } int main(){ scanf( "%d" , &N ) ; for( int i = 1 ; i <= N ; i ++ ) scanf( "%d" , &a[i] ) ; for( int i = 1 , u , v ; i < N ; i ++ ) scanf( "%d%d" , &u , &v ) , In( u , v ) ; preWork() ; solve() ; }