JZOJ4918. 最近公共祖先

这是一篇关于图论算法的博客,探讨了如何解决寻找最近公共祖先(LCA)并最大化权值的问题。在给定一棵n个节点的树中,存在m个操作,包括将节点染色和询问权值最大的LCA。通过线段树数据结构,可以实现O(nlogn)的时间复杂度来更新和查询贡献。

题目大意

给定一棵n个节点的树,根节点是1,每个点有一个点权vi。初始所有点都是白点。
m个操作:

  • 将一个点染为黑点
  • 询问一个点u找到除自己外的一个黑点v使得u,v的LCA的权值尽可能大,输出这个权值。

    Data Constraint
    n100000,m200000

    题解

    考虑单独计算每个点对其他点的贡献。
    对于一个修改操作x,首先x显然会对子树内的所有点造成贡献,如果x子树内之前没有黑点,那么x还要对父亲的其他子树造成贡献。
    所以线段树维护答案,做区间修改操作即可。

    时间复杂度:O(nlogn)

    SRC

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std ;
    
    #define N 100000 + 10
    struct Tree {
        int val , tag ;
    } T[4*N] ;
    
    bool Exist[N] ;
    int Node[2*N] , Next[2*N] , Head[N] , tot ;
    int W[N] , fa[N] , DFN[N] , R[N] ;
    int n , m , Cnt , ret ;
    
    void link( int u , int v ) {
        Node[++tot] = v ;
        Next[tot] = Head[u] ;
        Head[u] = tot ;
    }
    
    void DFS( int x ) {
        DFN[x] = R[x] = ++ Cnt ;
        for (int p = Head[x] ; p ; p = Next[p] ) {
            if ( Node[p] == fa[x] ) continue ;
            fa[Node[p]] = x ;
            DFS( Node[p] ) ;
            R[x] = R[Node[p]] ;
        }
    }
    
    void Update( int v ) {
        if ( T[v].tag <= 0 ) return ;
        int ls = v + v , rs = v + v + 1 ;
        T[ls].val = max( T[ls].val , T[v].tag ) ;
        T[rs].val = max( T[rs].val , T[v].tag ) ;
        T[ls].tag = max( T[ls].tag , T[v].tag ) ;
        T[rs].tag = max( T[rs].tag , T[v].tag ) ;
        T[v].tag = 0 ;
    }
    
    void Modify( int v , int l , int r , int x , int y , int value ) {
        if ( x > y ) return ;
        if ( l == x && r == y ) {
            T[v].val = max( T[v].val , value ) ;
            T[v].tag = max( T[v].tag , value ) ;
            return ;
        }
        Update( v ) ;
        int mid = (l + r) / 2 ;
        if ( y <= mid ) Modify( v + v , l , mid , x , y , value ) ;
        else if ( x > mid ) Modify( v + v + 1 , mid + 1 , r , x , y , value ) ;
        else {
            Modify( v + v , l , mid , x , mid , value ) ;
            Modify( v + v + 1 , mid + 1 , r , mid + 1 , y , value ) ;
        }
        T[v].val = max( T[v+v].val , T[v+v+1].val ) ;
    }
    
    void Search( int v , int l , int r , int x ) {
        if ( l == x && r == x ) {
            ret = T[v].val ;
            return ;
        }
        Update( v ) ;
        int mid = (l + r) / 2 ;
        if ( x <= mid ) Search( v + v , l , mid , x ) ;
        else Search( v + v + 1 , mid + 1 , r , x ) ;
        T[v].val = max( T[v+v].val , T[v+v+1].val ) ;
    }
    
    int main() {
        freopen( "lca.in" , "r" , stdin ) ;
        freopen( "lca.out" , "w" , stdout ) ;
        scanf( "%d%d" , &n , &m ) ;
        for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &W[i] ) ;
        for (int i = 1 ; i < n ; i ++ ) {
            int u , v ;
            scanf( "%d%d" , &u , &v ) ;
            link( u , v ) ;
            link( v , u ) ;
        }
        DFS( 1 ) ;
        memset( T , -1 , sizeof(T) ) ;
        for (int i = 1 ; i <= m ; i ++ ) {
            char op[10] ;
            scanf( "%s" , op + 1 ) ;
            if ( op[1] == 'M' ) {
                int x ;
                scanf( "%d" , &x ) ;
                Modify( 1 , 1 , n , DFN[x] , R[x] , W[x] ) ;
                if ( Exist[x] ) continue ;
                Exist[x] = 1 ;
                while ( fa[x] ) {
                    int y = fa[x] ;
                    Modify( 1 , 1 , n , DFN[y] , DFN[x] - 1 , W[y] ) ;
                    Modify( 1 , 1 , n , R[x] + 1 , R[y] , W[y] ) ;
                    if ( Exist[y] ) break ;
                    Exist[y] = 1 ;
                    x = y ;
                }
            } else {
                int x ;
                scanf( "%d" , &x ) ;
                ret = -1 ;
                Search( 1 , 1 , n , DFN[x] ) ;
                printf( "%d\n" , ret ) ;
            }
        }
    }

    以上.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值