hdu 4912Paths on the tree (LCA)

本文详细介绍了如何解决HDOJ 4912题目的方法,通过构建树结构并求解最近公共祖先(LCA),对查询进行排序,并在遍历时应用标记来避免重复选择路径。提供了一个高效的算法实现,包括初始化、深度优先搜索、RMQ查询和标记路径等关键步骤。

hdu 4912Paths on the tree (LCA)

解法:求出询问的lca,把询问根据lca的深度排序。深度深的先选,选了之后,把以当前询问的lca为根的子树全部打上vis标记,后选的边如果左右两端点有任何一个是有vis标记的就不选。

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std ;

const int maxn = 111111 ;

struct Edge {
    int to , next ;
} edge[maxn<<1] ;
int head[maxn] , tot , f[maxn<<1] ;

void new_edge ( int a , int b ) {
    edge[tot].to = b ;
    edge[tot].next = head[a] ;
    head[a] = tot ++ ;
}

struct LCA {
    int dp[22][maxn<<1] ;
    int to[maxn] , tim[maxn] ;
    int tot , n ;
    
    int MIN ( int a , int b ) {
        return tim[a] < tim[b] ? a : b ;
    }

    void init () {
        tot = 0 ;
        n = 0 ;
    }

    void dfs ( int u , int fa ) {
        tim[u] = ++ tot ;
        for ( int i = head[u] ; i != -1 ; i = edge[i].next ) {
            int v = edge[i].to ;
            if ( v == fa ) continue ;
            dfs ( v , u ) ;
            dp[0][++n] = u ;
        }
        dp[0][++n] = u ;
        to[u] = n ;
    }

    void rmq () {
        for ( int i = 1 ; i <= 20 ; i ++ ) {
            for ( int j = 1 ; j + (1<<i) - 1 <= n ; j ++ ) {
                dp[i][j] = MIN ( dp[i-1][j] , dp[i-1][j+(1<<i-1)] ) ;
            }
        }
    }
    
    int query ( int a , int b ) {
        a = to[a] , b = to[b] ;
        if ( a > b ) swap ( a , b ) ;
        int k = b - a + 1 ;
        return MIN ( dp[f[k]][a] , dp[f[k]][b-(1<<f[k])+1] ) ;
    }

} lca ;

int vis[maxn] , deep[maxn] ;

void dfs ( int u , int fa ) {
    deep[u] = deep[fa] + 1 ;
    for ( int i = head[u] ; i != -1 ; i = edge[i].next ) {
        int v = edge[i].to ;
        if ( v == fa ) continue ;
        dfs ( v , u ) ;
    }
}

void mark ( int u ) {
    vis[u] = 1 ;
    for ( int i = head[u] ; i != -1 ; i = edge[i].next ) {
        int v = edge[i].to ;
        if ( vis[v] || deep[u] > deep[v] ) continue ;
        mark ( v ) ;
    }
}

struct ANS {
    int l , r , u ;
    bool operator < ( const ANS& p ) const {
        return deep[u] > deep[p.u] ;
    }
} fuck[maxn] ;

void init () {
    memset ( head , -1 , sizeof ( head ) ) ;
    tot = 0 ;
    memset ( vis , 0 , sizeof ( vis ) ) ;
}

int main () {
    int n , m ;
    f[0] = -1 ;
    for ( int i = 1 ; i < maxn<<1 ; i ++ )
        f[i] = f[i>>1] + 1 ;
    while ( scanf ( "%d%d" , &n , &m ) != EOF ) {
        init () ;
        lca.init () ;
        for ( int i = 1 ; i < n ; i ++ ) {
            int a , b ;
            scanf ( "%d%d" , &a , &b ) ;
            new_edge ( a , b ) ;
            new_edge ( b , a ) ;
        }
        lca.dfs ( 1 , 0 ) ;
        lca.rmq () ;
        dfs ( 1 , 0 ) ;
        for ( int i = 1 ; i <= m ; i ++ ) {
            scanf ( "%d%d" , &fuck[i].l , &fuck[i].r ) ;
            fuck[i].u = lca.query ( fuck[i].l , fuck[i].r ) ;
        }
        sort ( fuck + 1 , fuck + m + 1 ) ;
        int ans = 0 ;
        for ( int i = 1 ; i <= m ; i ++ ) {
            if ( !vis[fuck[i].l] && !vis[fuck[i].r] ) {
                ans ++ ;
                mark ( fuck[i].u ) ;
            }
        }
        printf ( "%d\n" , ans ) ;
    }
    return 0 ;
}
/*
7 10
1 3
1 5
2 3
3 4
5 6
5 7

12 1000
1 2
1 4
1 5
2 9
2 3
4 8
5 6
5 11
5 12
6 7
6 10

7 3
1 2
1 3
2 4
2 5
3 6
3 7
2 3
4 5
6 7

*/


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值