求出v-DCC之后进行缩点,缩点变成一个森林。(题目直接给出边,但其实1~Max之间的所有点都是在这张图里面的)
森林可能会有单独的点,也会有树。
初始数量为res1 = 0,初始方案为res2 = 1
- 对于单点,res1++, res2 *= 1
- 对于缩点之后变成一个点: 该联通块内一定大于两个点,为了预防第一个出口被销毁,我们要再设置第二个出口,所以这类点的贡献:res1+=2, res2 *= (size * (size-1))/2
- 对于树,有两种点联通分量(设连通分量大小为size):
- 一种是作为叶子节点的存在,他们只与一个割点相连,所以对于这类点: res1++, res2 *= (size - 1) (减去1是因为要除去该联通分量内的那个割点)
- 另外一种是非叶子节点的存在,他们与两个或者更多的割点相连,由于每个树内肯定有多于一个的叶子节点,所以当这些割点中的某个点被销毁时,总能找到另外一个叶子节点与之相连,这就保证了在这样一个联通分量内,不设置出口是完全可以的(对于这类情况,可以画一棵树来帮助理解)。所以对于这类点不产生贡献
#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 1000 + 5;
const int M = 1005;
int head[N], ver[M], nxt[M], dfn[N], low[N], st[N], cut[N], sta[N];
int top, tot, cnt, num;
int n, m, root;
vector<int> dcc[N];
void init(){
memset(head, 0, sizeof head);
memset(low, 0, sizeof low);
memset(dfn, 0, sizeof dfn);
memset(cut, 0, sizeof cut);
memset(sta, 0, sizeof sta);
num = top = cnt = 0;
for (int i = 0; i < N;i++)
dcc[i].clear();
tot = 1;
}
void add(int x,int y){
ver[++tot] = y, nxt[tot] = head[x], head[x] = tot;
}
void tarjan(int x){
dfn[x] = low[x] = ++num;
st[++top] = x;
if(x == root && head[x] == 0){ // 孤立点
dcc[++cnt].push_back(x);
return ;
}
int flag = 0;
for(int i=head[x];i;i=nxt[i]){
int y = ver[i];
if(!dfn[y]){
tarjan(y);
low[x] = min(low[x], low[y]);
if(low[y] >= dfn[x]){
flag ++;
if(x != root || flag > 1)cut[x] = true;
cnt++;
int z;
do{
z = st[top--];
dcc[cnt].push_back(z);
}while(z != y);
dcc[cnt].push_back(x);
}
}else low[x] = min(low[x], dfn[y]);
}
}
int main() {
int cas = 0;
while(~scanf("%d",&m)){
if(m == 0)
break;
init();
int maxn = 0;
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
sta[x] = sta[y] = 1;
maxn = max(maxn, max(x, y));
}
int res1 = 0;
ull res2 = 1;
for (int i = 1; i <= maxn;i++){
if(!dfn[i] && sta[i]){
root = i;
tarjan(i);
}
else if(sta[i] == 0) res1++;
}
for (int i = 1; i <= cnt;i++){
int now = 0;
for (int j = 0; j < dcc[i].size();j++){
if(cut[dcc[i][j]])
now++;
}
if(now > 1) continue;
else if(now == 1){
res1++;
res2 *= (dcc[i].size() - 1);
} else {
res1 += 2;
res2 *= (dcc[i].size() * (dcc[i].size() - 1)) / 2;
}
}
printf("Case %d: %d %llu\n", ++cas, res1, res2);
}
return 0;
}