传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2730
思路:点双缩点
对于一个点双,如果它不与任何一个割点相连,那它就要建两个出口
与一个相连,就见一个
与两个以上相连就不用建
方案就是每个点双去掉割点的点数之积
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=1010,maxm=maxn<<1;
typedef long long ll;
using namespace std;
int n,m,now[maxn],son[maxm],pre[maxm],T;
int low[maxn],dfn[maxn],tim,tot,cas,deg,cnt,num,vis[maxn],ans1,root;bool cut[maxn];
ll ans2;
void clear(){
memset(now,0,sizeof(now)),memset(low,0,sizeof(low)),memset(dfn,0,sizeof(dfn));
memset(cut,0,sizeof(cut)),memset(vis,0,sizeof(vis)),tot=n=tim=T=ans1=0,cas++,ans2=1;
}
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
void tarjan(int x,int fa){
dfn[x]=low[x]=++tim;//printf("%d\n",x);
for (int y=now[x];y;y=pre[y]){
if (son[y]==fa) continue;
if (!dfn[son[y]]) tarjan(son[y],x),low[x]=min(low[x],low[son[y]]);
else{low[x]=min(low[x],dfn[son[y]]);continue;}
//printf("fuckpp%d %d\n",x,son[y]);
if (dfn[x]<=low[son[y]]){if (x==root) deg++;else cut[x]=1;}
}
}
void dfs(int x){
//printf("%d\n",x);
vis[x]=T;if (cut[x]) return; cnt++;
for (int y=now[x];y;y=pre[y]){
if (cut[son[y]]&&vis[son[y]]!=T) num++,vis[son[y]]=T;
if (!vis[son[y]]) dfs(son[y]);
}
}
int main(){
scanf("%d",&m);
while (m){
clear();
for (int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x),n=max(x,n),n=max(n,y);
for (int i=1;i<=n;i++){
if (!dfn[i]){tarjan(root=i,0);if (deg>=2) cut[root]=1;}//root要特判,因为要有两个才算割点
deg=0;
}
//for (int i=1;i<=n;i++) {printf("%d %d %d\n",i,dfn[i],low[i]);if (cut[i]) printf("%d\n",i);}
for (int i=1;i<=n;i++)
if (!vis[i]&&!cut[i]){
++T,cnt=num=0,dfs(i);//T时间戳,cnt:块的大小,num:块中割点数
if (!num) ans1+=2,ans2*=cnt*(cnt-1)/2;
if (num==1) ans1++,ans2*=cnt;
}
printf("Case %d: %d %lld\n",cas,ans1,ans2);
scanf("%d",&m);
}
return 0;
}