题目大意:
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
思路:
显然只有在割点坍塌的时候整张图的连通性才会有影响,否则其它所有人都可以任意走动。
如果一个连通块没有一个割点的话,那么就要放置两个,因为有可能是一个放置点塌掉了。
如果存在割点我们只考虑割点坍塌的情况,一个割点坍塌,那么整张图必定会变成两个连通块,所以我们要保证任意一个割点坍塌分成的两个连通块两边都至少有一个出口。
然后分分情况讨论一下,如果这个点双内只有一个割点的话,那么就必定要放一个出口。
如果有多个割点的话,那么其中任意一个坍塌了之后都可以沿着另外的割点到图的最边缘的刚才设置的出口去,所以这种连通块没有必要放置。
然后乘法原理计算一下就好了。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj2730.in","r",stdin);
freopen("bzoj2730.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=500+10;
int ca,m;
int beg[maxn],to[maxn<<1],las[maxn<<1],cnte=1;
int dfn[maxn],low[maxn],cnt_dfn,vis[maxn];
bool ty[maxn];
ll tot,ans,cnt0,cnt1;
void add(int u,int v){
las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;
las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u;
}
void tarjan(int u,int f,int rt){
int cnt=0;
low[u]=dfn[u]=++cnt_dfn;
for(int i=beg[u];i;i=las[i]){
if(to[i]==f)continue;
if(!dfn[to[i]]){
tarjan(to[i],u,rt);
low[u]=min(low[u],low[to[i]]);
if(u!=rt && low[to[i]]>=dfn[u])ty[u]=1;
else if(u==rt)++cnt;
if(u==rt && cnt>=2)ty[u]=1;
}
else low[u]=min(low[u],dfn[to[i]]);
}
}
void dfs(int u,int f,int rt){
++cnt0; vis[u]=rt;
for(int i=beg[u];i;i=las[i]){
if(to[i]==f)continue;
if(ty[to[i]]){
if(vis[to[i]]!=rt){
vis[to[i]]=rt;
++cnt1;
}
continue;
}
if(vis[to[i]])continue;
dfs(to[i],u,rt);
}
}
int main(){
File();
while(~scanf("%d",&m)){
if(!m)break;
ans=1; cnte=1; cnt_dfn=tot=0;
memset(beg,0,sizeof(beg));
memset(dfn,0,sizeof(dfn));
memset(ty,0,sizeof(ty));
memset(vis,0,sizeof(vis));
int u,v;
REP(i,1,m)read(u),read(v),add(u,v);
REP(i,1,500)if(beg[i] && !dfn[i])tarjan(i,0,i);
REP(i,1,500)if(beg[i] && !vis[i] && !ty[i]){
cnt0=cnt1=0;
dfs(i,0,i);
//cout<<cnt0<<" "<<cnt1<<endl;
if(!cnt1)ans=ans*cnt0*(cnt0-1)/2,tot+=2;
else if(cnt1==1)ans=ans*cnt0,++tot;
}
printf("Case %d: %lld %lld\n",++ca,tot,ans);
//cout<<endl<<endl;
}
return 0;
}