HNOI2012——矿场搭建(求割点)
描述
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N<=500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
输出
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
样例输入
9
1 3
4 1
3 5
1 2
2 6
1 5
6 3
1 6
3 2
6
1 2
1 3
2 4
2 5
3 6
3 7
0样例输出
Case 1: 2 4
Case 2: 4 1提示
Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);
Case 2 的一组解为(4,5,6,7)。
本来其实是一道很简单的板子
但是需要考虑的细节有点多
对于一个点双联通分量
如果它只通过一个割点与其他点相连
那这里面就必须要建一个出口
因为有可能会是割点断了
而如果有两个以上的割点就没必要了
无论哪一个断了都可以走另一个逃离
而如果整个图都是同一个双联通的话
却必须要2个出口(如果出口坍塌了)
所以要特判一下
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
char ch=getchar();
int res=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res;
}
int n,m,adj[1005],nxt[1005],to[1005],dfn[1005],low[1005],cut[1005],deg[1005],vis[1005],calc[1005],T,ans1,ans2,cnt,tot,num[1005];
inline void addedge(int u,int v){
nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
nxt[++cnt]=adj[v],adj[v]=cnt,to[cnt]=u;
}
stack<int> stk;
inline void tarjan(int u){
dfn[u]=low[u]=++tot;
int flag=0;
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v]){
if(++flag>1||u!=1)cut[u]=1;//what is the use of "u!=1"
}
}
else low[u]=min(low[u],dfn[v]);
}
}
inline void dfs(int u){
vis[u]=1,num[cnt]++;
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
if(!vis[v]&&!cut[v])dfs(v);
else if(cut[v]&&!calc[v])deg[cnt]++,calc[v]=1;
}
}
signed main(){
m=read();
while(m){
memset(adj,0,sizeof(adj));
memset(nxt,0,sizeof(nxt));
memset(to,0,sizeof(to));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(num,0,sizeof(num));
memset(deg,0,sizeof(deg));
memset(vis,0,sizeof(vis));
memset(cut,0,sizeof(cut));
++T;
n=0,cnt=0,tot=0,ans1=0,ans2=1;
for(int i=1;i<=m;i++){
int u=read(),v=read();
n=max(n,max(u,v));
addedge(u,v);
}
tarjan(1);
cnt=0;
for(int i=1;i<=n;i++){
if(!vis[i]&&!cut[i]){
memset(calc,0,sizeof(calc));++cnt,dfs(i);
if(deg[cnt]<2)ans1++,ans2*=num[cnt];
}
}
if(cnt==1)cout<<"Case "<<T<<": "<<2<<" "<<(n*(n-1)/2)<<'\n';
else cout<<"Case "<<T<<": "<<ans1<<" "<<ans2<<'\n';
m=read();
}
}