题目链接HDU3394
题目大意:
N个点,M条即将要修建的铁路。现在问,为了环形游览N个点。问我们有多少条路径是冲突的。定义冲突路径为:它不仅仅包含于一个环里面。如果一条路径不属于任何一个环,那么没有必要修建它。询问我们不必要修建的铁路数和冲突的铁路数。
分析:
显然不必要修建的铁路数就是该无向图中桥的数目。为什么?
对于一个环中的边,我们移除任意一条边,任然可以实现环中节点可达性。如果一个图移除掉了桥,其联通分量会增加。即桥不包含于任何一个环,因此没必要修建为桥(割边)的铁路。
而对于冲突边,其必然在点双连通分量中出现,如果一个块的点数等于边数,说明其是一个环,没有冲突边,但如果边数大于点数,说明这个块里至少有两个环,简单分析知此时块内所有边均为冲突边,所以问题转变为求桥的数量以及每个块的点数和边数,用Tarjan求点双连通分量,顺便就可以求出桥数,每找到一个块,就统计块内点的数量以及这些点之间连的边数,判断边数是否大于点数,如果大于则将边数累加到答案中
#include<bits/stdc++.h>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#define LL long long
#define Re register int
using namespace std;
const int N=11003,M=101000;
int n,m,x,y,T,Q_o,tmp,ans1,cut[N],bridge;LL ans2;//cut标记割点
std::vector<int>dcc[N];//缩点后第i个块存的原先的节点编号
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
int flag[N];
inline int max(Re a,Re b){return a>b?a:b;}
struct Tarjan{
int o,t,dfn_o,Q[N],dfn[N],low[N],head[N];
struct QAQ{int to,next;}a[M<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void CL(){
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(cut,0,sizeof(cut));
dfn_o=t=o=0;
}
inline void Sum(Re x){
memset(flag,0,sizeof(flag));
int rnm=0;
for(int j=0;j<dcc[x].size();j++){
flag[dcc[x][j]]=1;
}
for(int j=0;j<dcc[x].size();j++){
for(int k=head[dcc[x][j]];k;k=a[k].next){
if(flag[a[k].to])rnm++;
}
}
rnm=rnm/2;
if(rnm>dcc[x].size())ans1+=rnm;
}
inline void tarjan(Re x,Re rt){
Re son=0;Q[++t]=x,dfn[x]=low[x]=++dfn_o;
for(Re i=head[x],to;i;i=a[i].next){
if(a[i].to==rt)continue;
if(!dfn[to=a[i].to]){
tarjan(to,x),++son;
low[x]=min(low[x],low[to]);
if(low[to]>=dfn[x]){
if(low[to]>dfn[x])bridge++;
if(x!=rt)cut[x]=1;
else cut[x]|=son>1;
++Q_o;
while(1){
dcc[Q_o].push_back(Q[t]);
if(to==Q[t--])break;
}
dcc[Q_o].push_back(x);
Sum(Q_o);
}
}
else low[x]=min(low[x],dfn[to]);
}
}
inline void SuoPoint(){for(Re i=1;i<=n;++i)if(!dfn[i])tarjan(i,i);}
}T1;
int main(){
while(scanf("%d%d",&n,&m)){
if(n<=0&&m<=0){break;}
bridge=0;
ans1=0;
for(Re i=1;i<=Q_o;++i)dcc[i].clear();
T1.CL(),Q_o=ans1=0;
while(m--){in(x),in(y);T1.add(x,y),T1.add(y,x),n=max(n,max(x,y));}
T1.SuoPoint();
printf("%d %d\n",bridge,ans1);
}
}