hdu 3394 Railway 边双联通分量

本文详细介绍了一种求解双连通分量(BCC)的模板实现方法,并通过一道具体的题目来展示如何利用BCC算法解决寻找桥和特定边的问题。文章提供了完整的代码示例,解释了关键步骤和注意事项。

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3394

 

题意:

       给你n个城市和m条边,现在要你找到桥的数量以及,如果一个边双联通分量内部边数大于点数,要把所有的边数都加上。

做法:

       bcc模板,将边放入栈中进行维护,为了防止自环的情况还特意加了child判断儿子的数量。。写的时候一直wa因为忘记了判断可能有森林的情况只是dfs了点1,留个板子。


#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=10005;
const int maxm=100005;
typedef  pair<int,int> pii;
struct node{
    int from,to,next;
}e[maxm<<1];
stack<int> sa;
int now,head[maxn],bccnum,ans1,ans2,ef[maxm<<1],bj[maxm<<1];
int dfn[maxn],low[maxn],belong[maxn],n,m,cnt,top,st[maxm<<1];
void add(int u,int v){
    ef[now]=0;
    e[now].from=u,e[now].to=v;
    e[now].next=head[u],head[u]=now++;
}

//有自环时不加自环的边
//缩点方法:清空路径,枚举E[]数组中存储的路径,建立双向边
void tarjan(int root,int fa){
    dfn[root]=low[root]=++cnt;
	//新点初始化
    //int child=0;
//初始节点需要两个以上儿子且dfn[root]<=low[v]才是割点
    for(int i=head[root];~i;i=e[i].next){
        int v=e[i].to;
        if(ef[i])continue;
        ef[i]=ef[i^1]=1;
        st[++top]=i;//边入栈
        if(!dfn[v]){
            //child++;
            tarjan(v,root);
            low[root]=min(low[root],low[v]);
            if(dfn[root]<=low[v]){
//此点是割点,需注意初始节点要有两个儿子
                bccnum++;
                 if(dfn[root]<low[v]) ans1++;
//注意这里N++,建数组时要注意开至少两倍大
                int vsum=0,esum=0;
                for(;;){
                    int j=st[top--];
//bj[]数组用来标记节点所属的bcc,割点会改变,无意义。E[]存新图的边,esum是其数量,tarjan结束后建双向边
                    esum++;
                    if(bj[e[j].to]!=bccnum){
                        bj[e[j].to]=bccnum;
                        vsum++;
                        //E[++esum]=make_pair(edge[j].v,N);
                    }
                    if(bj[e[j^1].to]!=bccnum){
                        bj[e[j^1].to]=bccnum;
                        vsum++;
                        //E[++esum]=make_pair(edge[j^1].v,N);
                    }
                    if(i==j)break;
                }
                if(esum>vsum) ans2+=esum;
            }
        }
        else low[root]=min(low[root],dfn[v]);
    }
       //if(root==fa&&child<2)isCut[root]=0;
    //如果初始节点没有两个以上的儿子,标记清零 isCut[i]=1表示该点是割点
}

void init(){
    memset(head,-1,sizeof(head)); now=0; cnt=0;
    bccnum=0;  memset(bj,0,sizeof(bj));
    memset(dfn,0,sizeof(dfn));
    memset(belong,0,sizeof(belong));
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        if(n==0&&m==0) break;
        int u,v;
        init();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            u++,v++;
            add(u,v); add(v,u);
        }
        ans2=0,ans1=0;
        for(int i=1;i<=n;i++) {
            if(!dfn[i]) tarjan(i,-1);
        }
        printf("%d %d\n",ans1,ans2);
    }
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值