2730: [HNOI2012]矿场搭建
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 3111 Solved: 1469
[Submit][Status][Discuss]
Description
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
Input
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
Output
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
Sample Input
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
Sample Output
Case 1: 2 4
Case 2: 4 1
HINT
Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);
Case 2 的一组解为(4,5,6,7)。
当看到
使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
我们就明白这可能是一道与割点有关的题
对于一个没有割点的图,我们随意设置两个出口即可,方案自然是
C2n
C
n
2
了,但是对于有割点的图,我们要对各个联通块分类讨论(因为割点将图分为很多个联通块),如果这个联通块只有一个割点,那么我们必须在这里设置一个出口,但如果有两个割点,我们则不用设置出口,因为它必然可以通向其它有出口的联通块,方案数使用乘法原理即可
注意:①本题的n需要自己通过取MAX来求,题目只给了边数(如果你说万一中间故意设置一些点没有使用呢…好吧那还要处理一下)②将数组和变量清零③方案数初始应当为1
AC Code:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define rg register
#define il inline
#define maxn 5005
#define lid id << 1
#define rid (id << 1) | 1
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
struct edge{
int to , next;
}e[maxn << 1];
int head[maxn] , cnt , tot , siz , top , q[maxn] , num , dfn[maxn] , low[maxn] , idx;
bool vis[maxn] , cut[maxn];
int pa[maxn];
void add(int u,int v){
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int x,int fa){
rg int now;
rg int child = 0;
dfn[x] = low[x] = ++idx;
vis[x] = 1;
for (rg int i = head[x] ; i ; i = e[i].next){
rg int to = e[i].to;
if (to == fa) continue;
if (!vis[to]){
++child;
pa[to] = x;
dfs(to , x);
low[x] = min (low[x] , low[to]);
if ((!pa[x] && child > 1) || (pa[x] && low[to] >= dfn[x]) ) cut[x] = 1,++tot;
}
else low[x] = min(low[x] , dfn[to]);
}
}
void tarjan(int n){
for (rg int i = 1 ; i <= n ; ++i) if (!vis[i]) dfs(i , 0);
}
void dfs2(int u,int bj){
dfn[u] = bj;
++siz;
for (rg int i = head[u] ; i ; i = e[i].next){
rg int to = e[i].to;
if (cut[to] && dfn[to] != bj) ++num,dfn[to] = bj;
else if (!dfn[to]) dfs2(to , bj);
}
}
int main(){
rg int n , m , t = 0;
while (~scanf("%d",&m) && m){
++t;
memset(head , 0 , sizeof(head));
memset(low , 0 , sizeof(low));
memset(vis , 0 , sizeof(vis));
memset(dfn , 0 , sizeof(dfn));
memset(cut , 0 ,sizeof(cut));
memset(pa , 0 ,sizeof(pa));
num = siz = cnt = top = idx = 0;
rg int u , v;
n = tot = 0;
for (rg int i = 1 ; i <= m ; ++i){
u = read() , v = read();
add(u , v);
add(v , u);
n = max ( max (n , u) , v);
}
tarjan(n);
if (!tot) {cout<<"Case "<<t<<": "<<"2"<<' '<<n * (n - 1) / 2<<endl;continue;}
rg int ans1 = 0;
rg ll ans2 = 1;
memset(dfn , 0 ,sizeof(dfn));
for (rg int i = 1 ; i <= n;++i){
if (!dfn[i] && !cut[i]){
num = siz = 0;
dfs2(i , i);
if (num == 1) ans1++ , ans2 *= siz;
}
}
cout<<"Case "<<t<<": "<<ans1<<' '<<ans2<<endl;
}
return 0;
}