题解——home(必经路tarjan)

Posted ssw02

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解——home(必经路tarjan)相关的知识,希望对你有一定的参考价值。

题解——home(必经路tarjan)

校内胡策题9月29日,ssw02估分150,实际得分9,这道题估分100,实际爆零

题面搬运

题面讲述了一个ssw02(假)在学校逃课,老师来抓ssw02的故事。

输入:T组数据,每组数据一个图
输出:T组数据,每组输出一定可以抓到ssw02的点的个数和点。

思路

首先我们想到,这些可以抓到ssw02的点一定是整个图的割点(如果你这没想到,可以去重新学习一下割点)。

然后我们发现,啊,不对,有的割点好像走都不会走。然后我们加上以下代码,去找了一下那些点走过。

void  dfs( int u , int fa )
   anc[ u ] = fa , vis[ u ] = true ;
   for( register int i = head[ u ] ; i ; i = nex[ i ] )
       if( to[ i ] == fa )continue ;
       if( vis[ to[ i ] ] )continue ; 
       dfs( to[ i ] , u ) ;
   

int now = N ; vis[ now ] = true ;//main中
while( now != 1 )vis[ now ] = true , now = anc[ now ] ;

然后,秒切样例,一交,100少了个0,只有10。

我们来想想为什么?

给出下面一组数据:

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

仔细想想,发现我们可能先遍历5号点,再遍历6号点,然后5,6都通过了check。

本质原因错在哪? --我们并没有保证 5 , 6 不在一个连通块内。

然后Tarjan神仙又出现了,你没看错,tarjan在处理割点时还可以处理满足固定路径上点的不连通问题。

我们这次先跑dfs得出一条简单路径,再跑tarjan,并且在tarjan判断割点上加上如下代码

if( u != root || flag > 1 )cut[ u ] = 1 ;

这样我们发现,会被之前已在搜索树上的点更新的目标点并不会被判断为必经路的割点(很显然割掉这个点后,目标点还和搜索树相连)

御坂20001号( SXK )还有这样的理解,供大家参考

然后没了。注意题目给的范围有坑。M>=2*N

代码

#include<bits/stdc++.h>
using namespace std;
const  int  MAXN = 400010  ;
int  head[ MAXN ] , to[ MAXN*2 ] , nex[ MAXN*2 ] ;
int  dfn[ MAXN ] , low[ MAXN ] , anc[ MAXN ];
int  T , N , M , tot = 1 , num , root ;
bool vis[ MAXN ] , cut[ MAXN ] ;
inline int read()
    int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
    while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;

void  add( int x , int  y )
    to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ;

void  tarjan( int u )
    dfn[ u ] = low[ u ] = ++num ;
    int  flag = 0 ; 
    for( register int i = head[ u ] ; i ; i = nex[ i ] )
        int y = to[ i ] ;
        if( !dfn[ y ] )
            tarjan( y ) ;
            low[ u ] = min( low[ u ] , low[ y ] ) ;
            if( low[ y ] >= dfn[ u ] && vis[ y ] )
                flag++ ;
                if( u != root || flag > 1 )cut[ u ] = 1 ;
            
        
        else low[ u ] = min( low[ u ] , dfn[ y ] ) ;
    

void  dfs( int u , int fa )
    anc[ u ] = fa , vis[ u ] = true ;
    for( register int i = head[ u ] ; i ; i = nex[ i ] )
        if( to[ i ] == fa )continue ;
        if( vis[ to[ i ] ] )continue ; 
        dfs( to[ i ] , u ) ;
    

void  work()
    N  = read() , M = read() ;
    for( register int i = 1 ; i <= M ; i++ )
        int  m1 = read() , m2 = read() ;
        if( m1 == m2 )continue ;
        add( m1 , m2 ) , add( m2 , m1 ) ; 
    
    dfs( 1 , 1 ) ; for( register int i = 1 ; i <= N ; ++i )vis[ i ] = false ; 
    int now = N ; vis[ now ] = true ;
    while( now != 1 )vis[ now ] = true , now = anc[ now ] ;
    for( register int i = 1 ; i <= N ; i++ )
        if( !dfn[ i ] )
            root = i ; tarjan( i );  
        
    int  ans = 0 ;
    for( register int i = 2 ; i < N ; i++ )
        if( cut[ i ] )ans++ ;
    printf("%d\\n",ans);
    for( register int i = 2 ; i < N ; i++ )
        if( cut[ i ]  )
            printf("%d ",i);cut[ i ] = 0 ;
        
    printf("\\n") ;
    for( register int i = 1 ; i <= N ; ++i )
        vis[ i ] = dfn[ i ] = low[ i ] = head[ i ] = anc[ i ] = 0 ;
    tot = 1 , num = 0 ;

int  main()

    freopen("home.in","r",stdin);
    freopen("home.out","w",stdout);
    T = read() ; 
    while( T-- )work() ;
    return  0 ;

/*
1
5 5
1 2
2 3
3 4
4 5
4 1
*/

以上是关于题解——home(必经路tarjan)的主要内容,如果未能解决你的问题,请参考以下文章

P3387 模板缩点 题解 (Tarjan)

[BZOJ 1179]ATM题解 Tarjan缩点+SPFA

洛谷 P2194 HXY烧情侣Tarjan缩点 分析+题解代码

10-8 王小呆的校内互坑赛题解

小白入门向tarjan算法+codevs1332题解报告

poj 3177 Redundant Paths(tarjan边双连通)