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;
}
posted @ 2009-01-20 12:27  Beetlebum  阅读(506)  评论(2编辑  收藏  举报