题目链接:http://lightoj.com/volume_showproblem.php?problem=1300
边双连通分量首先dfs找出桥并标记,然后dfs交叉着色找奇圈上的点。这题只要求在奇圈上的点个数。容易得到,一个边双联通分量如果存在奇圈,那么整个分量上的点都属于某个奇圈。
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<queue> #include<vector> using namespace std; const int maxn = 11150; const int maxe = 30100; const int INF = 0x3f3f3f; int pre[maxn],low[maxn],dfs_clock; bool isbridge[maxe*2]; struct Edge{ int u,v; int next; Edge(int u=0,int v=0,int next=0): u(u) , v(v), next(next) {} }edges[maxe*2]; int head[maxn],cnt; int color[maxn]; //用交叉染色法进行判断奇圈; bool flag; int n,m; int ans,tempans; void tarjan(int u,int fa){ pre[u] = low[u] = dfs_clock++; for(int i=head[u];i!=-1;i=edges[i].next){ int v = edges[i].v; if(!pre[v]){ tarjan(v,u); low[u] = min(low[u],low[v]); if(low[v] > pre[u]) { isbridge[i] = true; isbridge[i^1] = true; } } else if(pre[v] < pre[u] && v != fa) //u->v是反向边; low[u] = min(low[u],pre[v]); } } void dfs_paint(int u,int fa_c){ color[u] = fa_c; tempans++; for(int i=head[u];i!=-1;i=edges[i].next){ if(isbridge[i]) continue; //访问到桥跳过; int v = edges[i].v; if(!color[v]) dfs_paint(v,3-fa_c); //还没有染色; else if(color[u] == color[v]) flag = true; } } void addedge(int u,int v){ edges[cnt] = Edge(u,v,head[u]); head[u] = cnt++; } int main() { // freopen("E:\acm\input.txt","r",stdin); int T; cin>>T; for(int t=1;t<=T;t++){ cin>>n>>m; memset(head,-1,sizeof(head)); cnt = 0; for(int i=1;i<=m;i++){ int a,b; scanf("%d %d",&a,&b); edges[cnt] = Edge(a,b,head[a]); head[a] = cnt++; edges[cnt] = Edge(b,a,head[b]); head[b] = cnt++; //addedge(a,b); //addedge(b,a); } dfs_clock = 1; memset(pre,0,sizeof(pre)); memset(isbridge,0,sizeof(isbridge)); for(int i=0;i<n;i++) if(!pre[i]) tarjan(i,-1); memset(color,0,sizeof(color)); ans = 0; for(int i=0;i<n;i++){ if(!color[i]){ flag = false; tempans = 0; dfs_paint(i,1); //交叉染色法 if(flag) ans += tempans; } } printf("Case %d: %d ",t,ans); } }