//无向图的双连通分量
//每次调用前手动清空vector<int> G
//使用时只更新G完成构图
//bcc_cnt从1开始计数
//pre[]表示点在DFS树中的先序时间戳
//iscut[]表示点是否为割点
//bccno[]表示点所在的双连通分量编号
//bcc_cnt表示双连通分量个数
//vector<int> G保存每个点相邻的下一个点序号
//vector<int> bcc保存每个双连通分量的点集,算法运行过程动态清空
//stack<Edge> S是算法用到的栈
const int MAXV = 51000;
const int MAXE = 51000;
int pre[MAXV], iscut[MAXV], bccno[MAXV], dfs_clock, bcc_cnt; // 割顶的bccno无意义
struct Edge
{
int u, v;
};
vector<int> G[MAXV], bcc[MAXV];
stack<Edge> S;
int dfs(int u, int fa)
{
int lowu = pre[u] = ++dfs_clock;
int child = 0;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
Edge e = (Edge){u, v};
if(!pre[v]) // 没有访问过v
{
S.push(e);
child++;
int lowv = dfs(v, u);
lowu = min(lowu, lowv); // 用后代的low函数更新自己
if(lowv >= pre[u])
{
iscut[u] = true;
bcc_cnt++;
bcc[bcc_cnt].clear();
for(;;)
{
Edge x = S.top();
S.pop();
if(bccno[x.u] != bcc_cnt)
{
bcc[bcc_cnt].push_back(x.u);
bccno[x.u] = bcc_cnt;
}
if(bccno[x.v] != bcc_cnt)
{
bcc[bcc_cnt].push_back(x.v);
bccno[x.v] = bcc_cnt;
}
if(x.u == u && x.v == v) break;
}
}
}
else if(pre[v] < pre[u] && v != fa)
{
S.push(e);
lowu = min(lowu, pre[v]); // 用反向边更新自己
}
}
if(fa < 0 && child == 1) iscut[u] = 0;
return lowu;
}
void find_bcc(int n)
{
// 调用结束后S保证为空,所以不用清空
memset(pre, 0, sizeof(pre));
memset(iscut, 0, sizeof(iscut));
memset(bccno, 0, sizeof(bccno));
dfs_clock = bcc_cnt = 0;
for(int i = 0; i < n; i++)
if(!pre[i]) dfs(i, -1);
};
int main()
{
// freopen("in.txt", "r", stdin);
int n, a, b, kase = 1;
while (~RI(n) && n)
{
REP(i, MAXV) G[i].clear();
int Max = 0;
REP(i, n)
{
RII(a, b); a--; b--;
Max = max(Max, max(a, b));
G[a].push_back(b);
G[b].push_back(a);
}
find_bcc(Max);
LL ans = 1, cnt = 0;
if (bcc_cnt == 1)
{
LL m = bcc[1].size();
ans = m * (m - 1) / 2;
cnt = 2;
}
else
{
FE(i, 1, bcc_cnt)
{
int tcnt = 0;
REP(j, bcc[i].size())
{
if (iscut[bcc[i][j]])
tcnt++;
}
if (tcnt == 1)
{
ans *= (bcc[i].size() - 1);
cnt++;
}
}
}
printf("Case %d: ", kase++);
cout << cnt << ‘ ‘ << ans << endl;
}
return 0;
}Mining Your Own Business,布布扣,bubuko.com
原文:http://blog.csdn.net/wty__/article/details/22385565