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 ;
}