HDU 5215 Cycle --- 奇偶环的判定

题意:

给出一个无向图,n<=100000,m <= 300000,判断图中是否有奇圈,是否有偶圈。

思路:

对于奇圈的判定,可以利用二分图染色。若此图是二分图,那么其中一定不存在奇圈,若此图不是二分图,那必定存在奇圈。若原图不连通,则对每个连通分量分别做二分图染色
对于偶圈的判定,首先考虑所有圈必然在边双连通分量中(题中要求的圈不一定是简单圈),边双连通分量之间是不存在圈的(因为桥的存在)。将原图用tarjan做边双连通分量分解。对于每个边双连通分量,其中必然存在至少一个圈。若此连通分量仅仅是一个圈,那么可以直接判断这个圈的奇偶性。若此连通分量是多个圈的叠加,考虑其中有公共部分的两个圈a,b,他们的奇偶性只有三种:奇奇,奇偶,偶偶。后两种已经存在偶圈,不考虑。对于第一种情况,我们一定可以通过取这两个圈的非公共边组成一个更大的偶圈。综上,若此连通分量是多个圈的叠加,则一定存在偶圈。
也就是说此边双连通分量有偶圈,当且仅当此边双连通分量是一个单一的偶圈或者是多个圈的叠加。单一的圈的判定方法:可以先求出边双连通分量中的点数a,和边数b,若a==b则是单一圈,否则是多圈叠加。

个人注意:

在求边双连通分量的过程中,tarjan标记桥别忘了反向边也要标记。

代码

#include 
#include 
#include 
#include 
#pragma comment(linker,"/STACK:102400000,102400000") 
using namespace std;
const int maxn = 2e5;
struct edge{int to,isb,rev;};//isb标记桥,rev记录反向边
int n,m;
vector G[maxn];
int color[maxn],dfs_clock,pre[maxn],low[maxn],vis[maxn],iscut[maxn];
void add_edge(int a,int b)
{
    edge u,v;
    u.to = b,u.isb = 0,v.to = a,v.isb = 0,u.rev = G[b].size(),v.rev = G[a].size();
    G[a].push_back(u);
    G[b].push_back(v);
}
bool bipartite(int u)
{
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i].to;
        if(!color[v])
        {
            color[v] = 3 - color[u];
            if(!bipartite(v))
                return false;
        }else if(color[v] == color[u])
            return false;
    }
    return true;
}
int dfs(int u,int fa)
{
    int lowu = pre[u] = ++dfs_clock;
    int child = 0;
    for(int i = 0; i < G[u].size();i++)
    {
        int v = G[u][i].to;
        if(!pre[v])
        {
            child++;
            int lowv = dfs(v,u);
            lowu = min(lowu,lowv);
            if(lowv > pre[u])
            {
                G[u][i].isb = 1;
                G[v][G[u][i].rev].isb = 1;
                // printf("%d -> %d\n",u,v);
            }
        }else if(pre[v] < pre[u] && v!=fa)
            lowu = min(lowu,pre[v]);
    }
    if(fa < 0 && child == 1) iscut[u] = 0;
    low[u] = lowu;
    return lowu;
}
int ncnt,mcnt;
void dfs2(int u)
{
//  printf("u = %d fa = %d\n",u,fa);
    for(int i = 0; i < G[u].size();i++)
    {
        if(G[u][i].isb)continue;
        int v = G[u][i].to;
        //printf("%d--%d\n",u,v);
        mcnt++;
    //  printf("%d -> %d\n",u,v);
        if(!vis[v])
        {
            ncnt++;
            vis[v] = true;
            dfs2(v);
        }
    }
}
int main()
{
    //freopen("0.in","r",stdin);
    //freopen("a.out","w",stdout);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        bool odd = false,even = false;
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n ;i++)
            G[i].clear();
        memset(color,0,sizeof(color));
        memset(pre,0,sizeof(pre));
        memset(vis,false,sizeof(vis));
        dfs_clock = 0;
        for(int i = 0; i < m ;i ++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v);
        }
        for(int i =1; i <= n; i++)
            if(!color[i])
            {
                color[i] = 1;
                if(!bipartite(i))
                    odd = true;
            }
        for(int i = 1; i <= n; i++)
            if(!pre[i])
                dfs(i,-1);
        for(int i = 1; i <= n; i++)
            if(!vis[i])
            {
                ncnt = 1,mcnt = 0;
                vis[i] = true;
                dfs2(i);
                mcnt/=2;
            //  printf("i = %d cnt = %d %d\n",i,ncnt,mcnt);
                if(ncnt >= 3)
                {
                    if(!(ncnt == mcnt && ncnt%2==1))
                    {
                        even = true;
                    }
                }
            }
        if(odd)
            printf("YES\n");
        else
            printf("NO\n");
        if(even)
            printf("YES\n");
        else
            printf("NO\n");
    }   
    return 0;
}

你可能感兴趣的:(解题报告,图论)