链接: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;
}