【COCI2007】追捕盗贼 (Standard IO)
Time Limits: 3000 ms Memory Limits: 65536 KB Detailed Limits
Description
为了帮助警察抓住在逃的罪犯,你发明了一个新的计算机系统。警察控制的区域有N个城市,城市之间有E条双向边连接,城市编号为1到N。
警察经常想在罪犯从一个城市逃亡另一个城市的过程中抓住他。侦查员在仔细研究地图,以决定在哪个城市设置障碍,或者断掉某条路。你的计算机系统必须回答以下两种问题:
1、 如果连接城市G1和G2的路被封掉,罪犯能否从城市A逃到城市B?
2、 如果城市C被封掉,罪犯能否从城市A逃到城市B?
编程实现上述计算机系统。
Input
输入第一行包含两个整数N和E(2<=N<=100000,1<=E<=500000),表示城市和边的数量。
接下来E行,每行包含两个不同的整数Ai和Bi,表示城市Ai和Bi之间有一条边直接相连,任意两个城市之间最多只有一条边相连。
接下来一行包含一个整数Q(1<=Q<=300000),表示询问次数。
接下来Q行,每行包含4个或5个整数,第一个数为1或2,表示询问问题的种类。
如果问题种类是1,后面跟着4个整数A,B,G1,G2,如上描述表示询问如果G1和G2之间的路封掉罪犯能否从城市A逃到城市B,保证A和B不同,G1和G2之间一定存在路。
如果问题种类是2,后面跟着三个整数A,B,C,三个数互不相同,表示询问如果封掉城市C,罪犯能否从城市A逃到城市B。
输入数据保证一开始任意两个城市都可以相互到达。
Output
每个询问输出一行“yes”或“no”。
Sample Input
13 15
1 2
2 3
3 5
2 4
4 6
2 6
1 4
1 7
7 8
7 9
7 10
8 11
8 12
9 12
12 13
5
1 5 13 1 2
1 6 2 1 4
1 13 6 7 8
2 13 6 7
2 13 6 8
Sample Output
yes
yes
yes
no
yes
这题训练tarjan很到位,既要找割点,也要找割边。对于割点要注意根的情况(如果不特判,根一定被认为是割点)。
思路是把块缩起来,构建新图,在新图上判断是否是子树的情况。对于u,v,若u是割的子树,v不是,则分开。我用了LCA判。不过似乎用dfs序判更好。傻了傻了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std ;
#define N 100010
#define M 500010
#define psb( x ) push_back( x )
#define iter vector< Br_info >::iterator
int n , Q , i , k , j , g[N] , T , m ;
struct edge {
int y , l ;
}f[M*2] , Gr[M*2] , Er[M*2] ;
map< int , int > G ;
void ins( int a , int b ) {
f[++T].y = b , f[T].l = g[a] , g[a] = T ;
}
int dfn[N] , low[N] , time , de[N] , fa[N] , sta[N] , top , Gt ;
int Ntid[N] , Etid[N] ;
struct Gstack {
int bridge , obj ;
}Gsta[N] ;
struct Br_info {
int a , b ;
};
vector< Br_info > Bri_info[N] ;
int h[N] ;
void Gins( int a , int b ) {
Gr[++T].y = b , Gr[T].l = h[a] , h[a] = T ;
Gr[++T].y = a , Gr[T].l = h[b] , h[b] = T ;
}
void Ndfs( int po ) {
int k , ori ;
dfn[po] = low[po] = ++time ;
sta[++top] = po ;
int ss = 0 ;
for( k=g[po] ; k ; k=f[k].l ) if( f[k].y != fa[po] ) {
ori = Gt ;
int anori = top ;
if( dfn[ f[k].y ] == 0 ) {
fa[ f[k].y ] = po ;
ss++ ;
Ndfs( f[k].y ) ;
if( low[ f[k].y ] >= dfn[po] ) {
while( anori != top ) {
Ntid[ sta[ top ] ] = f[k].y ;
top -- ;
}
while( Gt>ori ) {
Gins( f[k].y , Gsta[Gt].obj ) ;
Br_info pp ;
pp.a = f[k].y , pp.b = Gsta[Gt].obj ;
Bri_info[ Gsta[Gt].bridge ].psb( pp ) ;
Gt-- ;
}
Gsta[ ++Gt ].obj = f[k].y , Gsta[ Gt ].bridge = po ;
}
}
low[po] = min( low[po] , low[ f[k].y ] ) ;
}
if( po==1 ) {
Ntid[1] = Gsta[1].obj ;
for( k=Gt ; k>1 ; k-- ) {
Gins( Gsta[1].obj , Gsta[k].obj ) ;
Br_info pp ;
pp.a = Gsta[1].obj, pp.b = Gsta[k].obj ;
Bri_info[ Gsta[k].bridge ].psb( pp ) ;
}
}
}
int l[32][N] , inx[32] ;
void Pre( int po ) {
for( int k = 1 ; inx[k]<=de[po] ; k++ ) l[k][po] = l[k-1][ l[k-1][po] ] ;
for( int k = h[po] ; k ; k = Gr[k].l ) if( Gr[k].y != l[0][po] ) {
de[ Gr[k].y ] = de[po] + 1 ;
l[0][ Gr[k].y ] = po ;
Pre( Gr[k].y ) ;
}
}
int LCA( int a , int b ) {
if( de[a] > de[b] ) swap( a,b ) ;
for( int sub = de[b] - de[a] , j=0 ; sub ; j++,sub/=2 ) if( sub % 2 ) {
b = l[j][b] ;
}
if( a==b ) return a ;
int i ;
for( i=0 ; i>=0 ; ) {
for( i=30 ; i>=0 ; i-- ) if( l[i][a]!=l[i][b] ) {
a = l[i][a] , b = l[i][b] ;
break ;
}
}
return l[0][a] ;
}
bool Test( Br_info pp , int lca , int a ) {
if( LCA( a , pp.a ) != pp.a ) return 0 ;
if( LCA( a , pp.b ) != pp.b ) return 0 ;
if( LCA( lca , pp.a ) !=lca ) return 0 ;
if( LCA( lca , pp.b ) !=lca ) return 0 ;
return 1 ;
}
int Esta[N] , Et , e[N] ;
void Eins( int a , int b ) {
Er[++T].y = b , Er[T].l = e[a] , e[a] = T ;
Er[++T].y = a , Er[T].l = e[b] , e[b] = T ;
}
void Edfs( int po ) {
int k , ori ;
dfn[po] = low[po] = ++ time ;
sta[ ++top ] = po ;
for( k=g[po] ; k ; k=f[k].l ) if( fa[po] != f[k].y ) {
ori = Et ;
int anori = top ;
if( dfn[ f[k].y ] == 0 ) {
Edfs( f[k].y ) ;
}
low[po] = min( low[ f[k].y ] , low[po] ) ;
if( low[ f[k].y ] > dfn[po] ) {
while( top!=anori ) {
Etid[ sta[top--] ] = f[k].y ;
}
while( Et!=ori ) {
Eins( Esta[Et--] , f[k].y ) ;
}
Esta[ ++Et ] = f[k].y ;
}
}
if( po==1 ) {
while( top ) Etid[ sta[top--] ] = 1;
while( Et!=0 ) Eins( Esta[ Et-- ] , 1 ) ;
}
}
int El[32][N] , Ede[N] ;
int ELCA( int a , int b ) {
if( Ede[a] > Ede[b] ) swap( a,b ) ;
for( int sub = Ede[b] - Ede[a] , j=0 ; sub ; j++,sub/=2 ) if( sub % 2 ) {
b = El[j][b] ;
}
if( a==b ) return a ;
int i ;
for( i=0 ; i>=0 ; ) {
for( i=30 ; i>=0 ; i-- ) if( El[i][a]!=El[i][b] ) {
a = El[i][a] , b = El[i][b] ;
break ;
}
}
return El[0][a] ;
}
void Epre( int po ) {
for( int k = 1 ; inx[k]<=Ede[po] ; k++ ) El[k][po] = El[k-1][ El[k-1][po] ] ;
for( int k = e[po] ; k ; k = Er[k].l ) if( Er[k].y != El[0][po] ) {
Ede[ Er[k].y ] = Ede[po] + 1 ;
El[0][ Er[k].y ] = po ;
Epre( Er[k].y ) ;
}
}
int main() {
for( i=1 , inx[0]=1 ; i<=30 ; i++ ) inx[i] = inx[i-1] * 2 ;
scanf("%d%d",&n,&m ) ;
for( i=1 ; i<=m ; i++ ) {
int a , b ;
scanf("%d%d",&a,&b ) ;
ins( a , b ) , ins( b , a ) ;
}
T = 0 ;
Ndfs( 1 ) ;
memset( dfn , 0 , sizeof dfn ) ;
memset( low , 0 , sizeof low ) ;
time = 0 ;
top = 0 ;
T = 0 ;
Edfs( 1 ) ;
Pre( Gsta[1].obj ) ;
Epre( 1 ) ;
scanf("%d",&Q ) ;
while( Q-- ) {
int typ ;
scanf("%d",&typ ) ;
if( typ==2 ) {
int A , B , C ;
scanf("%d%d%d",&A,&B,&C ) ;
A = Ntid[A] , B = Ntid[B] ;
int lca = LCA( A , B ) ;
bool jud = 0 ;
for( iter it = Bri_info[C].begin() ; it!=Bri_info[C].end() ; it++ ) {
Br_info pp = *it ;
if( Test( pp , lca , A ) || Test( pp , lca , B ) ) {
jud = 1 ;
break ;
}
}
if( jud ) puts("no" ) ; else puts("yes" ) ;
} else {
int C[2] , A , B ;
scanf("%d%d%d%d",&A,&B,&C[0],&C[1] ) ;
A = Etid[A] , B = Etid[B] , C[0] = Etid[ C[0] ] , C[1] = Etid[ C[1] ] ;
if( C[1]==C[0] ) {
puts("yes" ) ;
continue ;
}
if( ELCA( C[1] , C[0] ) == C[0] ) swap( C[1] , C[0] ) ;// C[0] is the lower one
if( ELCA( A , C[0] ) == C[0] && ELCA( C[0] , B ) != C[0] || ELCA( B , C[0] ) == C[0] && ELCA( C[0] , A ) != C[0] ) puts("no" ) ;
else puts("yes" ) ;
}
}
}
DebugLog
用栈记录块的点时,ori=top的位置要放到找边循环内。