煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
Input
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
Output输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
Sample Input9 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 0Sample OutputCase 1: 2 4 Case 2: 4 1 Hint
Case 1 的四组解分别是 (2,4),(3,4),(4,5),(4,6) ;
Case 2 的一组解为 (4,5,6,7) 。
思路:要选逃生点,即是找割点,每个割点连接的连通块数量就是要设置的逃生点数,方案数就是每个连通块里的数量选1个,即C(x,1)
typedef long long LL; const int maxm = 5050; int dfn[maxm], low[maxm], dfsnum, iscut[maxm], cut, head[maxm], edgecnt, siz, num, kase; struct edge{ int u, v, nex; } edges[maxm]; void addedge(int u, int v) { edges[++edgecnt].u = u; edges[edgecnt].v = v; edges[edgecnt].nex = head[u]; head[u] = edgecnt; } void init() { dfsnum = cut = edgecnt = 0; memset(dfn, 0, sizeof(dfn)), memset(low, 0, sizeof(low)); memset(iscut, 0, sizeof(iscut)), memset(head, 0, sizeof(head)); } void tarjan(int u, int fa) { dfn[u] = low[u] = ++dfsnum; int child = 0, v; for(int i = head[u]; i; i = edges[i].nex) { v = edges[i].v; if(!dfn[v]) { child++; tarjan(v, u); low[u] = min(low[u], low[v]); if(!iscut[u] && ((low[v] >= dfn[u] && fa) || (!fa && child>1))) { iscut[u] = ++cut; } } else if(fa != v) { low[u] = min(low[u], dfn[v]); } } } void dfs(int u, int root) { siz++; dfn[u] = root; for(int i = head[u]; i; i = edges[i].nex) { int v = edges[i].v; if(iscut[v] && dfn[v] != root) { num++, dfn[v] = root; } else if(!dfn[v]) dfs(v, root); } } void run_case(int m) { init(); int n, u, v; n = 0; for(int i = 0; i < m; ++i) { cin >> u >> v; n = max(max(u, v), n); addedge(u, v), addedge(v, u); } for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i, 0); if(cut == 0) { cout << "Case " << ++kase << ": 2 " << n*(n-1)/2 << " "; return; } memset(dfn, 0, sizeof(dfn)); LL ans1 = 0, ans2 = 1; for(int i = 1; i <= n; ++i) { if(!dfn[i] && !iscut[i]) { siz = num = 0; dfs(i, i); if(num == 1) { ans1++; ans2 *= siz; } } } cout << "Case " << ++kase << ": " << ans1 << " " << ans2 << " "; } int main() { ios::sync_with_stdio(false), cin.tie(0); int m; while(cin >> m && m) run_case(m); return 0; }