首先,如果图本来就是一个点双联通的(即不存在割点),那么从这个图中选出任意两个点就OK了。
如果这个图存在割点,那么我们把割点拿掉后图就会变得支离破碎了。对于那种只和一个割点相连的块,这个块中至少要选一个点出来建逃生通道,而且可以任意选择,而对于那种和多个割点相连的块则没必要选点出来建逃生通道。
#include<stdio.h> #include<string.h> #include<algorithm> #define MAXN 100010 #define MAXM 100010 #pragma comment(linker, "/STACK:102400000,102400000") typedef long long LL; int dfn[MAXN], low[MAXN], h[MAXN], ind; bool vis[MAXN]; int N, M, first[MAXN], e, next[MAXM], v[MAXM], col[MAXN]; struct Edge { int x, y; }edge[MAXM]; void dfs(int u, int p, int o) { dfn[u] = low[u] = ++ ind; int cnt = 0; for(int i = first[u]; i != -1; i = next[i]) { if(v[i] == p) continue; if(!dfn[v[i]]) { ++ cnt; dfs(v[i], u, o); low[u] = std::min(low[u], low[v[i]]); if(u == o && cnt > 1) h[u] = 1; else if(u != o && low[v[i]] >= dfn[u]) h[u] = 1; } else low[u] = std::min(low[u], dfn[v[i]]); } } void tarjan() { for(int i = 1; i <= N; i ++) low[i] = dfn[i] = h[i] = 0; ind = 0; dfs(i, -1, i); } void add(int x, int y) { v[e] = y; next[e] = first[x], first[x] = e ++; } void input() { N = 0; for(int i = 0; i < M; i ++) { scanf("%d%d", &edge[i].x, &edge[i].y); N = std::max(edge[i].x, N); N = std::max(edge[i].y, N); } memset(first, -1, sizeof(first[0]) * (N + 1)), e = 0; for(int i = 0; i < M; i ++) add(edge[i].x, edge[i].y), add(edge[i].y, edge[i].x); } void find(int x, int c, int &pn, int &cn) { vis[x] = true, ++ pn; for(int i = first[x]; i != -1; i = next[i]) { int y = v[i]; if(vis[y]) continue; if(h[y]) { if(col[y] != c) col[y] = c, ++ cn; continue; } find(y, c, pn, cn); } } void process() { tarjan(); memset(vis, 0, sizeof(vis[0]) * (N + 1)); memset(col, 0, sizeof(col[0]) * (N + 1)); LL ans = 1; int cnt = 0; for(int i = 1; i <= N; i ++) if(!h[i] && !vis[i]) { int pn = 0, cn = 0; find(i, i, pn, cn); if(cn == 0) ans *= (LL)pn * (pn - 1) / 2, cnt += 2; else if(cn == 1) ans *= pn, ++ cnt; } printf("%d %I64d\n", cnt, ans); } int main() { int t = 0; while(scanf("%d", &M), M > 0) { input(); printf("Case %d: ", ++ t); process(); } return 0; }