http://acm.hdu.edu.cn/showproblem.php?pid=3394
题意:给定一个有N个点,M条边的无向图,求有多少条边没有在环内,有多少条边在至少2个环内。
思路:双连通求块,对于每个块,分别求出顶点数,记为a , 和边数,记为b,当a < b ,即顶点数小于边数的时候,则可以证明,这时候连通区域内的边都是clash边,当a == b的时候,则可以得出此时刚好是一个环,每条边都只在正好两个环内,当a > b的时候,这时候连通分量是一棵树,(其实只有两个顶点的时候才可能出现这种情况),此时的边都是没有构成环的边。
反思:一开始错误地以为可以对图进行缩点,然后用上面的规则求,这种方法是错误的,错误的根源就在于:在缩在一起的点中,不满足上面的规则,即当a < b的时候,并不是所有的边都是clash边,
比如: 1 ------ 2
| |
----3----
| |
4-------5
在上面的图中,每对顶点之间都有只有2条简单路径,就可以缩点,图中有5个顶点,6条边,但是6条边都不会出现clash的情况。因此要是用缩点就会出错。导致这种错误产生的原因就在于,求缩点的连通图中,只是要求任意两个结点之间有2条路径就可以了,而不能保证点连通性。
代码:
#include<stdio.h> #include<string.h> const int MAXN = 10010 ; const int MAXM = 100010 ; int N ,M ; struct Node{ int num,next ; }edge[MAXM*2] ; int root[MAXN] ,e_cnt ; void init(){ memset(root,-1, sizeof(root)); e_cnt = 0 ; } void add(int a ,int b){ edge[e_cnt].num = b ; edge[e_cnt].next = root[a]; root[a] = e_cnt ++ ; } int dfn[MAXN] , low[MAXN], stack[MAXN] ; int idx , top ,cnt ; int block[MAXN] ,res1,res2; bool vis[MAXN] ; void count(){ int a = block[0] ; int b = 0 ; for(int i=1 ;i<=block[0];i++){ int u = block[i] ; for(int j=root[u] ;j!=-1;j=edge[j].next){ int v = edge[j].num ; if( vis[v] ) b++ ; } } b /= 2 ; if( a > b ){ res1 += b ; } else if(a < b){ res2 += b ; } } void tarjin(int x, int pre){ low[x] = dfn[x] = ++idx ; stack[++top] = x ; for(int i=root[x] ;i!=-1;i=edge[i].next){ int u = edge[i].num ; if(u == pre) continue ; if( dfn[u] == -1){ tarjin(u,x) ; if( low[u] < low[x]) low[x] = low[u] ; if( low[u] >= dfn[x] ){ block[0] = 0 ; int v ; memset(vis,0,sizeof(vis)); do{ v = stack[top--] ; vis[v] = 1 ; block[ ++block[0] ] = v ; }while(u != v) ; block[ ++block[0] ] = x ; vis[x] = 1 ; count(); } } else if( dfn[u] < low[x] ) low[x] = dfn[u] ; } } void solve(){ idx = top = cnt = 0 ; memset(dfn , -1, sizeof(dfn)); res1 = res2 = 0 ; for(int i=1;i<=N;i++){ if( dfn[i] == -1 ){ tarjin(i , -1); } } printf("%d %d\n",res1,res2); } int main(){ int a, b ; while(scanf("%d%d",&N,&M) && (N+M)){ init() ; for(int i=1;i<=M;i++){ scanf("%d%d",&a,&b); a++ ;b ++ ; add(a,b); add(b,a); } solve() ; } return 0 ; }