首先,若是圖原本就是一個點雙聯通的(即不存在割點),那麼從這個圖中選出任意兩個點就OK了。spa
若是這個圖存在割點,那麼咱們把割點拿掉後圖就會變得支離破碎了。對於那種只和一個割點相連的塊,這個塊中至少要選一個點出來建逃生通道,並且能夠任意選擇,而對於那種和多個割點相連的塊則不必選點出來建逃生通道。code
#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; }