无向图的双连通分量

无向图的双连通分量

1.点双连通分量 v-DCC

性质:1.顶点数不超过2

2.图中任意两点都同时包含在至少一个简单环中

3.如果该分量里有一个奇圈,那么其他所有点也必然在某个奇圈中;含有一个奇圈的充要条件是该分量不是二分图。

注意:割点的bccno可以是有多种选择,不一定是想要的那一个

最小方案数问题

hdu3844题目大意:有n条路矿井,每条路连接两个顶点,没有重边。你的任务是在节点处安装太平井,不得不管哪个连接点倒塌,不在此连接点的所有旷工都能到达太平井(只有倒塌的那条路不能走)。问,选择安装太平井的最小数目,和该数目下的最小方案数。

# include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXN=1e5+100;
struct Edge{
   int u,v;
};
int dfn[MAXN],iscut[MAXN],bccno[MAXN],dfs_clock,bcc_cnt;
vector<int> G[MAXN],bcc[MAXN];
stack<Edge> S;
int dfs(int u,int fa)
{
   int lowu=dfn[u]=++dfs_clock;
   int child=0;
   int len=G[u].size();
   for(int i=0;i<len;++i){
       int v=G[u][i];
       Edge e=(Edge){u,v};
       if(!dfn[v]){
           S.push(e);
           child++;
           int lowv=dfs(v,u);
           lowu=min(lowu,lowv);
           if(lowv>=dfn[u]){
               iscut[u]=1;
               bcc_cnt++;
               for(;;){
                   Edge x=S.top(); S.pop();
                   if(bccno[x.u]!=bcc_cnt) { bcc[bcc_cnt].push_back(x.u); bccno[x.u]=bcc_cnt;}
                   if(bccno[x.v]!=bcc_cnt) { bcc[bcc_cnt].push_back(x.v); bccno[x.v]=bcc_cnt;}
                   if(x.u==u&&x.v==v) break;
              }
          }
      }else if(dfn[v]<dfn[u]&&v!=fa){
           S.push(e); lowu=min(dfn[v],lowu);
      }
  }
   if(fa<0&&child==1) iscut[u]=0;
   return lowu;
}
void find_bcc(int n)
{
   memset(dfn,0,sizeof(dfn));
   memset(iscut,0,sizeof(iscut));
   memset(bccno,0,sizeof(bccno));
   dfs_clock=bcc_cnt=0;
   for(int i=1;i<=n;++i){
       if(!dfn[i]) dfs(i,-1);
  }
}
void init(int n)
{
   for(int i=0;i<=2*n;++i){
       G[i].clear();
       bcc[i].clear();
  }
}
//v-DCC
int main()
{
   int cass=0,N;
   while(~scanf("%d",&N)&&N)
  {
       init(N);
       for(int i=1;i<=N;++i){
           int u,v; scanf("%d%d",&u,&v);
           G[u].push_back(v); G[v].push_back(u);
      }
       find_bcc(N);
       
       LL num=0,ans=1;
       if(bcc_cnt==1){//如果只有一个联通分量,那么只需要安装两个即可
           num=2;
           ans=1ll*bcc[1].size()*(bcc[1].size()-1)/2;
      }else{
           for(int i=1;i<=bcc_cnt;++i){
               int len=bcc[i].size();
               int cnt=0;
               for(int j=0;j<len;++j){
                   if(iscut[bcc[i][j]]) cnt++; //连通块内有多少个割点
              }
               if(cnt==1){//如果该联通分量只有一个割点,那么还要需要一个,否则一定出得去
                   num++;
                   ans=ans*1ll*(len-cnt);
              }
          }
      }
       printf("Case %d: %lld %lld\n",++cass,num,ans);
  }

   return 0;
}
/*
9
1 3
4 1
3 5
1 2
2 6
1 5
6 3
1 6
3 2
6
1 2
1 3
2 4
2 5
3 6
3 7
*/

缩点:

for i 1-> bcc_cnt:
if(iscut[x]){
add(i,now_id[x]);
add(now_id[x],i);
}

2.边双连通分量

性质:当且仅当任意一条边都包含在至少一个简单环中

poj3352题目大意:给你一个连通的无向图,现在问你最少在该图中添加几条边,能使得该图变成边双连通图?

对于一棵无向树,我们要使得其变成边双连通图,需要添加的边数 == (树中度数为1的点的个数+1)/2

# include <bits/stdc++.h>
using namespace std;

const int MAXN=1e3+100;
int dfn[MAXN],iscut[MAXN],bccno[MAXN],dfs_clock,bcc_cnt;
vector<int> G[MAXN],bcc[MAXN];
stack<int> S;
int dfs(int u,int fa)
{
   int lowu=dfn[u]=++dfs_clock;
   int child=0;
   int len=G[u].size();
   S.push(u);
   for(int i=0;i<len;++i){
       int v=G[u][i];
       if(!dfn[v]){
           child++;
           int lowv=dfs(v,u);
           lowu=min(lowu,lowv);
           if(lowv>dfn[u]){
               iscut[u]=1;
               //cout<<v<<" ----- "<<u<<endl;
               //v-u为桥
          }
      }else if(dfn[v]<dfn[u]&&v!=fa){
           lowu=min(dfn[v],lowu);
      }
  }
   if(lowu==dfn[u]){
       bcc_cnt++;
       for(;;){
           int v=S.top(); S.pop();
           bcc[bcc_cnt].push_back(v);
           bccno[v]=bcc_cnt;
           if(v==u) break;
      }
  }
   if(fa==-1&&child==1) iscut[u]=0;
   return lowu;
}
void find_bcc(int n)
{
   memset(dfn,0,sizeof(dfn));
   memset(iscut,0,sizeof(iscut));
   memset(bccno,0,sizeof(bccno));
   dfs_clock=bcc_cnt=0;
   for(int i=1;i<=n;++i){
       if(!dfn[i]) dfs(i,-1);
  }
}
void init(int n)
{
   for(int i=0;i<=n;++i){
       G[i].clear();
       bcc[i].clear();
  }
}
//e-DCC
int n,m;
int cnt[MAXN];
int main()
{
   while(~scanf("%d%d",&n,&m)){
       init(n);
       for(int i=1;i<=m;++i){
           int u,v;
           scanf("%d%d",&u,&v);
           G[u].push_back(v);
           G[v].push_back(u);
      }
       find_bcc(n);

       memset(cnt,0,sizeof(cnt));
       int ans=0;//联通分量里面有几条边
       for(int i=1;i<=n;++i){
           int len=G[i].size();
           for(int j=0;j<len;++j){
               int v=G[i][j];
               if(bccno[i]!=bccno[v]){
                   cnt[bccno[i]]++;
              }
          }
      }
       for(int i=1;i<=bcc_cnt;++i){
           if(cnt[i]==1) ans++;
      }
       printf("%d\n",(ans+1)/2);
  }
   return 0;
}
/*
9 11
1 2
2 3
3 4
4 5
2 5
1 5
1 6
6 9
6 7
6 8
8 9
*/

缩点:

if(bccno[x]==bccno[y]) continue;
else add(bccno[x],bccno[y]);



posted @ 2022-02-27 00:20  fengzlj  阅读(164)  评论(0)    收藏  举报