LA 3523 tarjian求双连通分量+二分判奇圈

白书上有详细解说

代码与书上有些不同,但核心是相通的

//LA 3523 点双连通+二分判奇圈
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <vector>
#include<stack>
#include <stack>
#define inf 0x3f3f3f3f
#define lowbit(x) ((x)&(-x))
#define maxn 1050
using namespace std;
int dfn[maxn],low[maxn],n,m,head[maxn],cnt,cntt,belong[maxn],ans,block,color[maxn],e[maxn][maxn],iscut[maxn],in[maxn];
vector<int> blo[maxn];
struct ee
{
    int next,to,fa;
}eage[maxn*maxn*2];
stack<ee> s;

void add(int a,int b)
{
    eage[cntt].to=b;
    //eage[cntt].w=c;
    eage[cntt].next=head[a];
    eage[cntt].fa=a;
    head[a]=cntt++;
}

void init()
{
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(belong,0,sizeof(belong));
    memset(iscut,0,sizeof(iscut));
    memset(in,0,sizeof(in));
    memset(e,0,sizeof(e));
    cnt=cntt=0;
    ans=0;
    block=0;
    while(!s.empty())
        s.pop();
    for(int i=0;i<=n;i++)
        blo[i].clear();
}

void tarjian(int a,int b)
{
    dfn[a]=low[a]=++cnt;
    int child=0;
    for(int i=head[a];i!=-1;i=eage[i].next)
    {
        int j=eage[i].to;
        if(j==b)
            continue;
        if(!dfn[j])
        {
            s.push(eage[i]);
            child++;
            tarjian(j,a);
            low[a]=min(low[j],low[a]);
            if(low[j]>=dfn[a])
            {
                block++;
                iscut[a]=1;
                while(1)
                {
                ee x=s.top();
                s.pop();
                if(belong[x.fa]!=block)
                {
                    belong[x.fa]=block;
                    blo[block].push_back(x.fa);
                }
                if(belong[x.to]!=block)
                {
                    belong[x.to]=block;
                    blo[block].push_back(x.to);
                }
                if(x.fa==a&&x.to==j)
                    break;
                }
            }
        }
        else if(dfn[j]<dfn[a])                                    //这个很关键如果不这样的话,s中会加两次回边。
            {s.push(eage[i]);low[a]=min(low[a],dfn[j]);}
    }
    if(b<0&&child==1)
        iscut[a]=0;
}

bool twojudge(int a,int b)
{
    for(int i=head[a];i!=-1;i=eage[i].next)
    {
        int j=eage[i].to;
        if(belong[j]==b)
        {
           if(color[j]==color[a])
                return false;
           if(!color[j])
           {
               color[j]=3-color[a];
               if(!twojudge(j,b))
                return false;
           }
        }
    }
    return true;
}

int main () {
   //freopen("d:\\in.txt","r",stdin);
   while(~scanf("%d%d",&n,&m)&&(n+m))
    {
        init();
        if(n==0&&m==0)
            break;
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            e[a][b]=1;
            e[b][a]=1;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
               if(!e[i][j]&&j!=i)
               {
                   add(i,j);
               }
        for(int i=1;i<=n;i++)
        {
            if(!dfn[i])
            {
                tarjian(i,-1);
            }
        }
        for(int i=1;i<=block;i++)
        {
            memset(color,0,sizeof(color));                                                  //由于割点会重复,所以要清空。
            for(int j=0;j<blo[i].size();j++)
                belong[blo[i][j]]=i;
            int u=blo[i][0];
            color[u]=1;
            if(!twojudge(u,i))
            {
               for(int j=0;j<blo[i].size();j++)
                in[blo[i][j]]=1;
            }
        }
        ans=n;
        for(int i=1;i<=n;i++)
            if(in[i])
               ans--;
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(LA 3523 tarjian求双连通分量+二分判奇圈)