hdu 3394 Railway
http://acm.hdu.edu.cn/showproblem.php?pid=3394
题意:给定一个有N个点,M条边的无向图,求有多少条边没有在环内,有多少条边在至少2个环内。
思路:画一下图,很容易就想到在一个双连通图里,如果边数>点数,则此双连通图里的边都是在2个环内的。其实是错的,下面数据就是这样的特例:
6 6
1 2
1 3
2 3
3 4
3 5
4 5
其中3为割点。主要就是处理这种数据,在tarjan中,u是v的父亲,从v回溯到u,如果low[v]>dfn[u],则说明v到u没有环,如果low[v]==dfn[u],则说明u到v有环;这两种情况都要处理。

#include<set> #include<map> #include<stack> #include<queue> #include<cmath> #include<bitset> #include<string> #include<climits> #include<cstdio> #include<vector> #include<utility> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define IN puts("in") #define OUT puts("out") #define FR(x) freopen(x,"r",stdin) #define FW(x) freopen(x,"w",stdout) #define ST system("pause") using namespace std; const int maxn = 10005; struct nd { int u,v,next; }edge[maxn*20]; int head[maxn],vis[maxn],dfn[maxn],low[maxn],as[maxn],st[maxn]; int ecnt,cnt,idx,tp,ans1,ans2; void add(int u,int v) { edge[ecnt].u = u; edge[ecnt].v = v; edge[ecnt].next = head[u]; head[u] = ecnt++; } void getans() { int i,u,v,j,a=0; for(i = 0; i < cnt; ++ i) { u = as[i]; for(j = head[u]; j != -1; j = edge[j].next) { v = edge[j].v; if(vis[v])a++; } } a /= 2; if(a>cnt) ans2 += a; if(a<cnt) ans1 += a; } void tarjan(int pre,int u) { int i,v,x; dfn[u] = low[u] = ++idx; st[++tp] = u; for(i = head[u]; i != -1; i = edge[i].next) { v = edge[i].v; if(v==pre)continue; if(!dfn[v]){ tarjan(u,v); low[u] = min(low[v],low[u]); if(low[v]>=dfn[u]) { cnt = 0; do{ x = st[tp--]; vis[x] = 1; as[cnt++] = x; }while(v!=x); as[cnt++] = u; vis[u] = 1; getans(); for(x=0;x<cnt;vis[as[x++]]=0); } }else low[u] = min(dfn[v],low[u]); } } int main() { int i,n,m,u,v; while(scanf("%d %d",&n,&m)==2) { if(n+m==0)break; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(head,-1,sizeof(head)); ecnt = cnt = idx = 0; for(i = 0; i < m; ++ i) { scanf("%d %d",&u,&v); add(u,v); add(v,u); } ans1 = ans2 = tp = 0; for(i = 0; i < n; ++ i)if(!dfn[i]) tarjan(-1,i); printf("%d %d\n",ans1,ans2); } return 0; }