追捕盗贼

【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
#include
#include
#include
#include

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的位置要放到找边循环内。

你可能感兴趣的:(追捕盗贼)