hdu 3896 Greatest TC 判断从a到b是否必须经过某点或某边

Problem Description
TC (Tian Chao) is magical place, as you all know...
The railways and the rail-stations in TC are fragile and always meet with different kinds of problems. In order to reach the destination safely on time, you are asked to develop a system which has two types of main functions as below.
1: A B C D, reporting whether we can get from station A to station B without passing the railway that connects station C and station D.
2: A B C, reporting whether we can get from station A to station B without passing station C.
Please notice that the railways are UNDIRECTED.
 

Input
For each test case, the first line will contain two integers N (2<=N<=100000) and M (1<=M<=500000), namely the number of stations and railways in TC. Then each of the next M lines will have two integers, describing the two stations that a certain railway is connecting. After this, there comes a line containing a single integer Q (Q<=300000), which means the number of queries to make on the system. The next Q lines will be queries. Each query begins with a integer, indicating the type of query, followed by 4 (the first type) or 3 (the second type) integers describing the details of the query as what mentioned above.
The stations are always labeled from 1 to N.
 

Output
For each test case, print "yes" or "no" in separated lines for the queries.
 

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

//

给出一个无向图,询问两个点在删去一条边或者一个点以后能否到达。

生成一颗dfs树,然后记录dfn[]、low[]、final[]、deep[]。Final表示离开这个节点的时间。
对于边的询问,询问的节点是a,b,设这个边为g1-g2,其中deep[g1]>deep[g2]。


1. a在g1的子树内,b也在g1的子树内,那么a、b可以到达。
2. a不在g1的子树内,b也不在g1的子树内,那么a、b可以到达。
3. 不妨假设a在g1的子树内,b不在g1的子树内,则判断low[g1]是否≤dfn[g2]。如果是的话,那么a、b可以到达。
4. 其他情况下不能到达。


对于点的询问,询问的节点是a,b,设这个点为g1:


1. 如果a,b都不在g1的子树内。则可行
2. 如果a,b有一个在g1的子树内(设为a),求出在a-g1路径上的倒数第二个点k,如果low[k] < dfn[g1]则可行
3. 如果a,b都在g1的子树内。求出在a-g1路径上的倒数第二个点k1,在b-g1路径上的倒数第二个点k2,判断是否两者的low都<dfn[g1]则可行
4. 其他情况下不能到达。

如何判断一个点a是否在g的子树内:
Dfn[a] >= dfn[g] 且 final[a] <= final[g]


#include <cstdio>
#include <cstring>
#include <cstdlib>
#define len1 100001
#define len2 1000001
#define min(x1,x2) ((x1)<(x2)?(x1):(x2))
#define swap(x1,x2) {int _t = x1 ; x1 = x2 ; x2 = _t;}


int dfn[len1] , low[len1] , final[len1] , hash[len1] , sm = 0 , timel = 0 , deep[len1] ;
int n , m , lim = 0 ;
int fa[20][len1] , ed[20] ;
struct edge
{
    int tot  ;
    int now[len1] , next[len2] , g[len2] ;


    void add( int st, int ed )
    {
        ++tot;
        g[tot] = ed ;
        next[tot] = now[st];
        now[st] = tot ;
    }
}e;




void dfs( int x , int ff , int dep )
{
    ++timel ;
    dfn[x] = low[x] = timel ;
    hash[x] = sm ;
    deep[x] = dep ;


    for ( int i = e.now[x] ; i ; i = e.next[i] )
    if ( e.g[i] != ff )
    {
        if ( hash[ e.g[i] ] != sm )
        {
            dfs( e.g[i] , x , dep+1 );
            low[x] = min( low[x] , low[ e.g[i] ] );
            fa[1][ e.g[i] ] = x ;
        }
        else
            low[x] = min( low[x] , dfn[ e.g[i] ] );
    }
    final[x] = ++timel ;
}
bool ini( int a , int b )
{
    if ( dfn[a] >= dfn[b] && final[a] <= final[b] )
        return true ;
    return false;
}
bool judge( int sc , int sv , int g1 , int g2 ) //  判断 sc , sv 在去掉g1-g2 后能否相连
{
    int fl = 0 ;


    if ( deep[g1] < deep[g2] )
        swap(g1,g2);
    if ( ini( sc , g1 ) && ini( sv , g1 ) )
        fl = 1 ;
    else if ( !ini( sc , g1 ) && !ini( sv , g1 ) )
        fl = 1 ;
    else if ( low[g1] <= dfn[g2] )
        fl = 1 ;
    return fl ;
}
int move( int x , int step )
{
    int t = x , p = 1;
    if ( step < 0 )
        return -1 ;
    while ( step )
    {
        if ( step & 1 )
            t = fa[p][t];
        step >>= 1 ;
        ++p;
    }
    return t ;
}


int main()
{
    FILE *in , *out;
    int sc , sv , g1 , g2 , q , t , t1 , t2 ;
    int fl = 1 ;




    //in = fopen("input.txt","r");
    //out = fopen("2.out","w");


    //fscanf(in,"%d%d",&n,&m);
    scanf("%d%d",&n,&m);
    ed[1] = 1 ;
    for ( int i = 2 ; ; ++i )
    {
        ed[i] = (ed[i-1]<<1);
        lim = i ;
        if ( ed[i] >= n )
            break;
    }
    for ( int i = 1 ; i <= m ; ++i )
    {
        scanf("%d%d",&sc,&sv);
        e.add( sc , sv );
        e.add( sv , sc );
    }
    ++sm  ;
    dfs( 1 , 1 , 1 );
    for ( int i = 2 ; i <= lim ; ++i )
        for ( int j = 1 ; j <= n ; ++j )
            fa[i][j] = fa[i-1][ fa[i-1][j] ];


    scanf("%d",&q);
    for ( int i = 1 ; i <= q ; ++i )
    {
        if ( i == 27 )
            i = 27 ;
        scanf("%d",&t);
        fl = 0 ;
        if ( t == 1 )
        {
            scanf("%d%d%d%d",&sc,&sv,&g1,&g2);
            fl = judge( sc , sv , g1 , g2 );
        }
        else
        {
            scanf("%d%d%d",&sc,&sv,&g1);
            if ( !ini( sc , g1 ) && !ini( sv , g1 ) )
                fl = 1 ;
            else if ( ini( sc , g1 )^ini( sv , g1 ) )
            {
                if ( ini( sc , g1 ) )
                {
                    t = move( sc , deep[sc] - deep[g1] - 1 );
                    if ( low[t] < dfn[g1] )
                        fl = 1 ;
                }
                else
                {
                    t = move( sv , deep[sv] - deep[g1] - 1 );
                    if ( low[t] < dfn[g1] )
                        fl = 1 ;
                }
            }
            else
            {
                t1 = move( sc , deep[sc] - deep[g1] - 1 );
                t2 = move( sv , deep[sv] - deep[g1] - 1 );
                if ( t1 == t2 )
                    fl = 1 ;
                else
                {
                    if ( low[t1] < dfn[g1] && low[t2] < dfn[g1] )
                        fl = 1;
                }
            }
        }
        if ( fl )
            printf("yes\n");
        else
            printf("no\n");
    }


    //fclose(in);
    //fclose(out);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值