题解:
无向图双联通分量
详见注释
标程:
#include<bits/stdc++.h>
using namespace std;
#define M(a) memset(a,0,sizeof(a))
#define v e[i].to
typedef long long ll;
const int N=503;
struct kk{
int to,ne;
}e[N*2];//双向建边,两倍
int tot,n,m,T,cnt,num,low[N],dfn[N],ans1,vis[N],cut[N],head[N],Case,x,y,tim,i;
ll ans2;
int read(){
int x=0;char ch;
do ch=getchar();while(ch<48||ch>57);
while(ch>=48&&ch<=57)x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x;
}
void add(int x,int y){
e[++tot].to=y;
e[tot].ne=head[x];
head[x]=tot;
}
void tarjan(int u,int fa){
int child=0;//u的孩子数量
dfn[u]=low[u]=++tim;
for (int i=head[u];i;i=e[i].ne)
if (!dfn[v]){
child++;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if (low[v]>=dfn[u]) cut[u]=1;
}else if (dfn[v]<dfn[u] && v!=fa) low[u]=min(low[u],dfn[v]);
if (child==1 && !fa) cut[u]=0;//若该点为根节点且只有一个儿子,就不是割点
}
void dfs(int u){
vis[u]=T;cnt++;
for (int i=head[u];i;i=e[i].ne){
if (vis[v]!=T && cut[v]) num++,vis[v]=T;
//刚开始把vis[v]!=T写成!vis[v],后来发现一个割顶可能会在多个联通块中出现
if (!vis[v] && !cut[v]) dfs(v);
}
}
int main(){
do{
m=read();
if (!m) return 0;
M(vis);M(cut);M(dfn);M(head);//偷了个懒,low不用清空
n=ans1=T=tot=tim=0;ans2=1;//清空,有多组数据
for (i=1;i<=m;i++) x=read(),y=read(),add(x,y),add(y,x),n=max(n,max(x,y));
for (i=1;i<=n;i++)
if (!dfn[i]) tarjan(i,0);
for (i=1;i<=n;i++)
if (!vis[i] && !cut[i]){
T++,cnt=num=0;
//cnt为当前联通块中总的点的个数,num为割点个数
dfs(i);
if (!num) ans1+=2,ans2*=cnt*(cnt-1)/2;
//内部无割点,则任意选两个出口
if (num==1) ans1++,ans2*=cnt;
//内部有一个割点,就任意选一个非割顶的点当作出口
}
printf("Case %d: %d %lld\n",++Case,ans1,ans2);
}while (1);
}