题意很容易弄懂,就是找到割点,以及割点连接的强连通分量的个数,简称连通数。用tarjan来
求,假设1为根结点,则连通数为0。其他点假设连通数为1。根据下面的定义来求。
if(low[pnt[i]] >= dfn[cur])
sub[cur] ++;
【割点】
在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联
的边以后,原图变成多个连通块,就称这个点集为割点集合。当割点集合的顶点个数只有1个时,该
顶点就是割点。
此时,我们可以得到割点的定义如下:
若有k的儿子为i,我们定义AnceDeep[i]为结点i辈分最高(深度最浅)的祖先的深度,deep[k]为k的搜索深度(时间戳),那么k为割点当且仅当k满足(1)(2)中的一个:
(1) 若k为深搜树的根Root,当且仅当k的儿子数(分支数)>=2时k为割点;
(2) 若k为搜索树的中间结点(即k既不为根也不为叶),那么k必然有father和son,若AnceDeep[son]>= deep[k],则k必然为割点。
对于(1)是显然的,根结点k一旦有2个以上的分支,那么删除k必然出现森林;
对于(2)比较难理解,首先注意AnceDeep[son]>= deep[k]这个条件,意思就是“k的儿子son的辈分最高的祖先(暂且设其为w)的深度,比k的深度要深(或者等于k的深度,此时k就是w),就是说 k的辈分比w更高(深度更浅),那么一旦删除k,son所在的网络势必和 k的father所在的网络断开”,那么k就是割点。
/*Accepted 196K 0MS C++ 1807B 2012-07-30 15:50:57*/ #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; const int MAXN = 1 << 10; const int MAXM = MAXN * MAXN; int dfn[MAXN], low[MAXN], sub[MAXN]; int cnt, e, N; int first[MAXN], next[MAXM], pnt[MAXM]; int x, y; void tarjan(int cur) { int i; dfn[cur] = low[cur] = ++ cnt; for(i = first[cur]; i != -1; i = next[i]) { if(!dfn[pnt[i]]) { tarjan(pnt[i]); if(low[pnt[i]] < low[cur]) low[cur] = low[pnt[i]]; if(low[pnt[i]] >= dfn[cur]) sub[cur] ++; } else if(dfn[pnt[i]] < low[cur]) low[cur] = dfn[pnt[i]]; } } void solve() { int i; memset(dfn, 0, sizeof dfn); for(i = 2; i <= N; i ++) sub[i] = 1; cnt = 0; sub[1] = 0; tarjan(1); int flag = 0; for(i = 1; i <= N; i ++) { if(sub[i] > 1){ flag = 1; printf(" SPF node %d leaves %d subnets\n", i, sub[i]); } } if(!flag) printf( " No SPF nodes\n"); } void addedge(int x, int y) { pnt[e] = y; next[e] = first[x], first[x] = e ++; } void ReadGraph() { e = 0; memset(first, -1, sizeof first); scanf("%d", &y); addedge(x, y), addedge(y, x); N = max(N, max(x, y)); for( ; ; ) { scanf("%d", &x); if(x == 0) break; scanf("%d", &y); addedge(x, y), addedge(y, x); N = max(N, max(x, y)); } } int main() { int cas = 0; while(scanf("%d", &x) != EOF) { if(x == 0) break; ReadGraph(); if(cas ++) printf("\n"); printf("Network #%d\n", cas); solve(); } return 0; }