先用Tarjan求割点。
可以想到,如果图中没有割点,那么可以在任意一个位置设置救援出口,这样看上去能合法,但是如果坍塌的点正好是救援出口就不行了。
因此,要设置
2
个救援出口,方案数为
如果有割点,则在每一个点双连通分量内分类讨论:
1、当前点双连通分量内只有一个割点:只需要设置
1
个救援出口,可以放在双连通分量内除割点外的任意节点上。这样,如果该双连通分量内的除救援出口之外的一个点坍塌,那么一定可以合法。如果救援出口坍塌,则此时因为救援出口不在割点上,因此该双连通分量内的点一定会与该双连通分量之外的点连通。
2、当前点双连通分量内的割点数量大于
救援出口数就是每个双连通分量的救援出口数之和。
方案数就是每个双连通分量的方案数的乘积。
代码:
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 6e5 + 5;
int n, m, ecnt, nxt[N], adj[N], go[N], times, dfn[N], low[N], top, stk[N],
tot, orzCkuohaoYkuohaoXbaosongqinghua, ysy[N], zzq[N]; vector<int> bel[N];
bool cut[N];
void add_edge(int u, int v) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
void cyx(int rt, int u) {
dfn[stk[++top] = u] = low[u] = ++times; int orz = 0;
for (int e = adj[u], v; e; e = nxt[e])
if (!dfn[v = go[e]]) {
orz++; cyx(rt, v); low[u] = min(low[u], low[v]);
if ((u == rt && orz > 1) || (u != rt && dfn[u] <= low[v]))
cut[u] = 1;
if (dfn[u] <= low[v]) {
bel[++tot].clear();
do {
bel[tot].push_back(stk[top--]);
} while (stk[top + 1] != v);
bel[tot].push_back(u);
}
}
else low[u] = min(low[u], dfn[v]);
}
void work() {
int i, j; n = ecnt = tot = times = top = 0;
for (i = 1; i <= m; i++) ysy[i] = read(), zzq[i] = read(),
n = max(n, max(ysy[i], zzq[i]));
for (i = 1; i <= n; i++) adj[i] = dfn[i] = cut[i] = 0;
for (i = 1; i <= m; i++) add_edge(ysy[i], zzq[i]);
for (i = 1; i <= n; i++) if (!dfn[i]) cyx(i, i);
int res = 0; ll ans = 1; for (i = 1; i <= tot; i++) {
int orz = 0, pyz = bel[i].size();
for (j = 0; j < pyz; j++) if (cut[bel[i][j]]) orz++;
if (!orz) res += 2, ans *= 1ll * pyz * (pyz - 1) / 2;
else if (orz == 1) res++, ans *= pyz - 1;
}
printf("Case %d: %d %lld\n", ++orzCkuohaoYkuohaoXbaosongqinghua, res, ans);
}
int main() {
while (m = read()) work();
return 0;
}