[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() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值