poj 3694 Network(边双连通+LAC)

题意:一个网络中有N台电脑,M条线路,现在任意两台电脑都可以互相通信,但是有些线路是重要的,如果破坏了他们,那么这个网络就不连通了,我们称这样的线路为“桥”,如果向网络中加入一些线路,问加入线路后的网络中有多少“桥”。

思路:先求出强连通分支,缩点形成一个树,如果加入线路的两点在同一分支中,那么这条线路就没什么用了,直接输出cnt就可以了,如果这两点不在同一分支中,那么就相当于向一根树中加入一条边,然后将从这两点到他们最近公共祖先的线路都去掉就是现在网络中的“桥”的数量。这题不限时间,LAC用最原始的方法都能过,因为对LAC的两种求法都不太熟悉,所以就用了最原始的方法。

代码:

View Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <math.h>
#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 ;

    //freopen( "input.txt" , "r" , stdin ) ;
    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 ;
}
原文地址:https://www.cnblogs.com/misty1/p/2772180.html