双连通分量入门——UVALive - 5135


无向图的双连通分量分为点双连通分量和边双连通分量,从定义上讲就是任意两条至少存在点不重复或者边不重复的路径,应用意义上来讲就是对于一个(点/边)双连通分量来说,删去(一个点/一条边)之后剩下的部分的连通性并没有受到影响,剩下的任意点之前还能互相到达,这就能衍生出很多应用了。

图论中的割点,割边,双连通分量,强连通分量等类型的题目都有比较简单易懂的dfs的算法,看起来也比较相似。

int dfs(int u, int fa){
    int lowu = pre[u] = ++clock;
    int child = 0;
    for (int i = 0; i < e[u].size(); i++){
        int v = e[u][i];
        if (!pre[v]){
            s.push({ u, v });
            int lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            if (lowv >= pre[u]){
                is[u] = 1;
                bcc_cnt++; bcc[bcc_cnt].clear();
                while (1){
                    pair<int, int> x = s.top(); s.pop();
                    if (bccno[x.first] != bcc_cnt){ bccno[x.first] = bcc_cnt; bcc[bcc_cnt].push_back(x.first); }
                    if (bccno[x.second] != bcc_cnt){ bccno[x.second] = bcc_cnt; bcc[bcc_cnt].push_back(x.second); }
                    if (x.first == u&&x.second == v)break;
                }
            }
        }
        else if (pre[v]<pre[u]&&v != fa){
            s.push({ u, v });
            lowu = min(lowu, pre[v]);
        }
    }
    if (fa < 0 && child == 1){ is[u] = 0; }
    low[u] = lowu;
    return lowu;
}

void find_bcc(int n){
    memset(pre, 0, sizeof(pre));
    memset(is, 0, sizeof(is));
    memset(bccno, 0, sizeof(bccno));
    clock = bcc_cnt = 0;
    for (int i = 0; i < n; i++){
        if (!pre[i])dfs(i,-1);
    }
}

这样处理完之后我们便能获得bcc_cnt个双连通分量,继而根据问题进行下一步处理。

Mining Your Own Business UVALive - 5135

问题描述:矿井由隧道和连接点连接而成,因为有塌方的危险,需要在某些连接点处安装安全井,我们要找出最小需要安装多少个安全井以及方案数。
1.简单来说题目要满足的就是不管哪一个点塌方,其他点都有通路通向安全井。如果图中存在割点,如果割点处塌方,便会出现新的连通分量,这部分孤立的子图若没有安全井,就跑不出去了。
2.所以我们的主要思路就是求出双连通分量,对每一个双连通分量进行处理,若这个双连通分量只由一个割点,就需要在这个连通分量非割点的地方安装一个安全井(割点塌方就直接去所在安全井,安全井塌方就从割点去其他双连通块)。若有不止一个割点,不管哪里塌方了都可以通过一个割点去其他双连通块,所以不用安装。
3.若图本身就是一个双连通分量,因为安全井所在的地方也有可能塌方
,所以需要安装两个安全井。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <math.h>
#include <bitset>
#include <list>
#include <algorithm>
#include <climits>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn = 50005;
int n, m;
vector<int> e[maxn];
vector<int> bcc[maxn];
stack<pair<int, int> > s;
int pre[maxn];
int low[maxn];
int deg[maxn];
int is[maxn];
int bccno[maxn];
int bcc_clock,bcc_cnt;
int dfs(int u, int fa){
    int lowu = pre[u] = ++bcc_clock;
    int child = 0;
    for (int i = 0; i < e[u].size(); i++){
        int v = e[u][i];
        if (!pre[v]){
            s.push({ u, v });
            child++;
            int lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            if (lowv >= pre[u]){
                is[u] = 1;
                bcc_cnt++; bcc[bcc_cnt].clear();
                while (1){
                    pair<int, int> x = s.top(); s.pop();
                    if (bccno[x.first] != bcc_cnt){ bccno[x.first] = bcc_cnt; bcc[bcc_cnt].push_back(x.first); }
                    if (bccno[x.second] != bcc_cnt){ bccno[x.second] = bcc_cnt; bcc[bcc_cnt].push_back(x.second); }
                    if (x.first == u&&x.second == v)break;
                }
            }
        }
        else if (pre[v]<pre[u]&&v != fa){
            s.push({ u, v });
            lowu = min(lowu, pre[v]);
        }
    }
    if (fa < 0 && child == 1){ is[u] = 0; }
    low[u] = lowu;
    return lowu;
}

void find_bcc(int n){
    memset(pre, 0, sizeof(pre));
    memset(is, 0, sizeof(is));
    memset(bccno, 0, sizeof(bccno));
    bcc_clock = bcc_cnt = 0;
    for (int i = 0; i < n; i++){
        if (!pre[i])dfs(i,-1);
    }
}

int main(){
    int x, y;
    int cas = 0;
    while (scanf("%d", &m), m){
        cas++;
        n = 0;
        for (int i = 0; i < maxn; i++){ e[i].clear(); }
        for (int i = 0; i < m; i++){
            scanf("%d%d", &x, &y);
            n = max(n, max(x, y));
            x--, y--;
            e[x].push_back(y);
            e[y].push_back(x);
        }
        find_bcc(n);
        long long ans1 = 0, ans2 = 1;
        if (bcc_cnt == 1){
            ans1 = 2; ans2 = (long long)n*(n - 1) / 2;
        }
        else{
            for (int i = 1; i <= bcc_cnt; i++){
                int cut_cnt = 0;
                for (int j = 0; j < bcc[i].size(); j++){
                    if (is[bcc[i][j]])cut_cnt++;
                }
                if (cut_cnt == 1){
                    ans1++; ans2 *= (long long)(bcc[i].size() - 1);
                }
            }
        }
        printf("Case %d: %lld %lld\n", cas, ans1, ans2);

    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值