SCU2016-02 B题 (缩点+LCA)

本文介绍了一种处理图论中强连通分支和桥边问题的算法。通过将强连通分支压缩为单个节点并构建一棵树,利用LCA算法找到每次加入边后形成的环,从而判断哪些边是桥边。该方法适用于动态图中的桥边维护问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析:

把强联通分支全部缩成点,变成一颗树,然后跑一下LCA,每一次加边都会在树上形成一个环,环上所有的边都将不再是桥,并且把环缩成点。

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define pr(x) cout << #x << ": " << x << "  " 
#define pl(x) cout << #x << ": " << x << endl;

#define  N 100005
#define  M 200005
using namespace std ;

struct node
{
    int e , next ;
}p[2*M+1000] ;

int head[N] , dfn[N] , low[N] , vist[N] , pre[N] ;
bool mark[N] ;
int n , m , num , cnt , id , q ;

void add( int x , int y )
{
    p[num].e = y ;
    p[num].next = head[x] ;
    head[x] = num++ ;

    p[num].e = x ;
    p[num].next = head[y] ;
    head[y] = num++ ;
}

void dfs( int x )
{
    vist[x] = 1 ;
    dfn[x] = low[x] = ++id ;
    for ( int i = head[x] ; i != -1 ; i = p[i].next )
    {
        int v = p[i].e ;
        if( !vist[v] )
        {
            pre[v] = x ;
            dfs( v ) ;
            low[x] = min( low[x] , low[v] ) ;
            if ( low[v] > dfn[x] )//map[x][v]就是桥
            {
                cnt++ ;
                mark[v] = true ;
            }
        }
        else if ( vist[v] && v != pre[x] )
        low[x] = min( low[x] , dfn[v] ) ;
    }
    //vist[x] = 2 ;
}

void find( int x , int y )
{
    while( dfn[x] > dfn[y] )
    {
        if ( mark[x] )
        {
            cnt-- ;
            mark[x] = false ;
        }
        x = pre[x] ;
    }
    while ( dfn[y] > dfn[x] )
    {
        if ( mark[y] )
        {
            cnt-- ;
            mark[y] = false ;
        }
        y = pre[y] ;
    }
    while ( x!= y )
    {
        if ( mark[x] )
        {
            cnt-- ;
            mark[x] = false ;
        }
        if ( mark[y] )
        {
            cnt-- ;
            mark[y] = false ;
        }
        x = pre[x] ;
        y = pre[y] ;
    }
}

int main()
{
    int i , x , y ;
    int cas = 1 ;

    while ( scanf ( "%d%d" , &n , &m ) , n + m )
    {
        memset( head , -1 , sizeof( head )) ;
        num = 0 ;
        for ( i = 1 ; i <= m ; i++ )
        {
            scanf ( "%d%d" , &x , &y ) ;
            add( x , y ) ;
        }
        memset( dfn , 0 , sizeof ( dfn )) ;
        memset( low , 0 , sizeof ( low )) ;
        memset( vist , 0 , sizeof ( vist )) ;
        memset( mark , false , sizeof ( mark )) ;
        //求强连通,缩点
        cnt = id = 0 ;
        for ( i = 1 ; i <= n ; i++ )
        if ( !dfn[i] )
        dfs( i );

        scanf ( "%d" , &q );
        printf ( "Case %d:\n" , cas++ ) ;
        while ( q-- )
        {
            scanf ( "%d%d" , &x , &y ) ;
            //两点是否在同一分支中
            if ( low[x] == low[y] )
            {
                printf ( "%d\n" , cnt ) ;
                continue ;
            }
            //求公共最近祖先
            find ( x , y );
            printf ( "%d\n" , cnt ) ;
        }
        printf ( "\n" ) ;
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值