pku3694 Network

题意是给出一个连通无向图,求每次加一条边后,图内割边的数目。

最容易想到的方法是每加一条边都做一次DFS求割边,于是code之,提交,TLE。

然后上网搜了一下,看到了一个更直观的方法:设新加入的边为(u,v),先求u和v的LCA,看从LCA分别到u和v的路径上有多少条割边,然后从原图的割边数目上累减,结果就是所求,因为每加一条边,该边与DFS树上的边形成了环,环内的边就不再是割边了。这样只需要做一次DFS。

 

#include  < iostream >
using   namespace  std;

#define  MAXN 100001
#define  Min(a,b) (a<b?a:b)

int  p[MAXN],ecnt,n,m,q,T = 1 ,lowlink[MAXN],dfn[MAXN],sign,cnt,odeg[MAXN],pnt[MAXN],cutp[MAXN];
bool  cut[MAXN],visited[MAXN];

struct  Edge{
    
int  v,next;
}edg[
10 * MAXN];

void  init(){
    ecnt
= 0 ;
    memset(p,
- 1 , sizeof (p));
    sign
= 0 ;
    memset(dfn,
- 1 , sizeof (dfn));
    memset(odeg,
0 , sizeof (odeg));
    cnt
= 0 ;
    memset(pnt,
- 1 , sizeof (pnt));
    memset(cut,
false , sizeof (cut));
    memset(cutp,
- 1 , sizeof (cutp));
}

void  dfs( int  pre, int  u){
    
int  i,v;
    lowlink[u]
= dfn[u] =++ sign;
    
for (i = p[u];i !=- 1 ;i = edg[i].next){
        v
= edg[i].v;
        odeg[u]
++ ;
        
if (dfn[v] !=- 1 ){
            
if (v != pre) //
                lowlink[u] = Min(lowlink[u],dfn[v]);
        }
        
else {
            dfs(u,v);
            pnt[v]
= u;
            lowlink[u]
= Min(lowlink[u],lowlink[v]);
            
if (dfn[u] < lowlink[v]){
                cnt
++ ;
                cut[u]
= true ;
                
// 存割边
                edg[ecnt].next = cutp[u];
                edg[ecnt].v
= v;
                cutp[u]
= ecnt ++ ;
            }
        }
    }
}



void  fun( int  u, int  v){ // 找LCA,求LCA分别到u和v的路径上的割边数目,比较暴力
     int  t,pt;
    
bool  find;
    memset(visited,
false , sizeof (visited));
    
int  x = pnt[u],y = pnt[v],LCA = 0 ;
    
// 找LCA
     while (x !=- 1 ){
        visited[x]
= true ;
        x
= pnt[x];
    }
    
while (y !=- 1 ){
        
if (visited[y]){
            LCA
= y;
            
break ;
        }
        y
= pnt[y];
    }
    
if (LCA == 0 )
        LCA
= 1 ;
    x
= u,y = v;
    
do {
        
if (cut[pnt[x]]){ // 若x的双亲是某一割边的起点,则(pnt[x],x)有可能是一条割边
            find = false ;
            pt
= t = cutp[pnt[x]];
            
while (t !=- 1 ){ // 看x是不是属于以pnt[x]为起点的割边的终点
                 if (edg[t].v == x){
                    find
= true ;
                    
if (pt == t) // 找到则从终点集中去掉x
                        cutp[pnt[x]] = edg[t].next;
                    
else
                        edg[pt].next
= edg[t].next;
                    
break ;
                }
                pt
= t;
                t
= edg[t].next;
            }
            
if (find)
                cnt
-- ;
            
if (cutp[pnt[x]] ==- 1 ) // 终点集为空,则pnt[x]不再是割边的起点
                cut[pnt[x]] = false ;
        }
        x
= pnt[x];
    }
while (x != LCA  &&  x !=- 1 );

    
do {
        
if (cut[pnt[y]]){
            find
= false ;
            pt
= t = cutp[pnt[y]];
            
while (t !=- 1 ){
                
if (edg[t].v == y){
                    find
= true ;
                    
if (pt == t)
                        cutp[pnt[y]]
= edg[t].next;
                    
else
                        edg[pt].next
= edg[t].next;
                    
break ;
                }
                pt
= t;
                t
= edg[t].next;
            }
            
if (find)
                cnt
-- ;
            
if (cutp[pnt[y]] ==- 1 )
                cut[pnt[y]]
= false ;
        }
        y
= pnt[y];
    }
while (y != LCA  &&  y !=- 1 );
}



int  main(){
    
int  i,u,v;
    
while (scanf( " %d%d " , & n, & m)  &&  n  &&  m){
        init();
        
for (i = 0 ;i < m;i ++ ){
            scanf(
" %d%d " , & u, & v);
            edg[ecnt].next
= p[u];
            edg[ecnt].v
= v;
            p[u]
= ecnt ++ ;
            edg[ecnt].next
= p[v];
            edg[ecnt].v
= u;
            p[v]
= ecnt ++ ;
        }
        dfs(
- 1 , 1 );
        scanf(
" %d " , & q);
        printf(
" Case %d:\n " ,T ++ );
        
for (i = 0 ;i < q;i ++ ){
            scanf(
" %d%d " , & u, & v);
            fun(u,v);
            printf(
" %d\n " ,cnt);
        }
        printf(
" \n " );
    }
    
return   0 ;
}

转载于:https://www.cnblogs.com/zgmf_x20a/archive/2009/01/20/1378846.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值