题解——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
*/