求点双连通分量

首先弄明白什么是点双连通分量.无向图中如果删掉一个点之后连通块数目变多,这个点叫做”割点”,删掉一条边后连通块增加则这条边为"桥".无向图dfs得到一棵搜索树,不在树上的边都认为是回向边(或者说反向边).
不存在割点的极大连通子图叫做无向图的双连通分量。由此定义,图中的桥和两端的两个点也组成了一个点双连通分量.
关键是那个dfs函数.dfn[x]表示x在dfs中被发现的时间.low[x]表示x沿着树边往下走再往上走一条反向边能够到达的点中dfn[]最小是多少
感觉我这个bcc板子比蓝书的短多了(雾)
poj2942 Knights of the Round Table
关于这个题怎么做,可以看蓝书.

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define pb(x) push_back(x)
typedef vector<int>::iterator pt;
const int maxn=1005;
int n,m;

vector<int> bcc[maxn];int bccN;

vector<int> to[maxn];
int E[maxn][maxn],eflag=0;
int dfn[maxn],low[maxn],prt[maxn],T;
int stk[maxn],top;

void dfs(int x){//这个函数是关键
  low[x]=dfn[x]=++T;
  for(pt i=to[x].begin();i!=to[x].end();++i){
    if(*i==prt[x])continue;//恰好沿着树边往回走?不可以的
    if(dfn[*i]==0){
      prt[*i]=x;
      stk[++top]=*i;//一个点对应一条树边,代表x到*i所连的树边
      dfs(*i);//递归搜索
      if(low[*i]>=dfn[x]){
	bcc[++bccN].clear();
	do{
	  bcc[bccN].pb(stk[top]);
	}while(stk[top--]!=*i);
	bcc[bccN].pb(x);//注意点x还要单独加到这个BCC中
      }
      if(low[*i]<low[x])low[x]=low[*i];//更新low[x]
    }

    if(dfn[*i]<low[x])low[x]=dfn[*i];//更新low[x]
  }
}
int good[maxn],gflag;
int ufs[maxn],col[maxn];
int find(int x){
  if(x==ufs[x])return x;
  int rt=find(ufs[x]);
  col[x]^=col[ufs[x]];
  return ufs[x]=rt;
}
int bccbel[maxn],belT=0;
bool link(int x,int y){
  int rx=find(x),ry=find(y);
  if(rx!=ry){
    ufs[rx]=ry;
    col[rx]=col[x]^col[y]^1;
    return false;
  }else{
    return col[x]==col[y];
  }
}
bool check(int x){
  ++belT;
  for(pt i=bcc[x].begin();i!=bcc[x].end();++i){
    ufs[*i]=*i;col[*i]=0;bccbel[*i]=belT;
  }
  for(pt i=bcc[x].begin();i!=bcc[x].end();++i){
    for(pt j=to[*i].begin();j!=to[*i].end();++j){
      if(bccbel[*j]==belT){
	if(link(*i,*j))return true;
      }
    }
  }
  return false;
}
int main(){
  while(scanf("%d%d",&n,&m),n!=0){
    for(int i=1;i<=n;++i)to[i].clear();
    ++eflag;
    for(int i=1,a,b;i<=m;++i){
      scanf("%d%d",&a,&b);
      E[a][b]=E[b][a]=eflag;
    }
    
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
	if(i!=j&&E[i][j]!=eflag)to[i].pb(j);

    memset(dfn,0,sizeof(dfn));
    memset(prt,0,sizeof(prt));
    memset(low,0,sizeof(low));
    top=0;T=0;bccN=0;
    for(int i=1;i<=n;++i){
      if(dfn[i]==0)dfs(i);
    }
    ++gflag;
    for(int i=1;i<=bccN;++i){
      if(check(i)){
	for(pt j=bcc[i].begin();j!=bcc[i].end();++j){
	  good[*j]=gflag;
	}
      }
    }
    int ans=0;
    for(int i=1;i<=n;++i){
      if(good[i]!=gflag)++ans;
    }
    printf("%d\n",ans);
  }
  return 0;
}

posted @ 2018-06-29 10:39  liu_runda  阅读(3257)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难