• Tarjan算法之边双联通分量


    定义

    若一个无向连通图不存在割边,则称它为“边双连通图”。无向连通图的极大边双连通子图被称为“边双连通分量”,简记为“e-DCC”

    定理

    一个图为边双连通图,当且仅当任意一条边都至少包含在一个简单环中。

    求法

    把图中所有桥删除,剩下的都是e-DCC。
    具体方法:一般用Tarjan标记所有桥边,再用dfs求出各连通块个数(遍历时不走桥)。
    常和缩点搭配:把e-DCC的编号当做节点,桥当做节点间的连边,则会形成一棵树或一座森林。

    例题冗余路径

    经典应用-构造边双连通分量:树的每一条边都是桥,但是给任意不同且不直接相连的两点加上一边后两点与其lca构成一个环,环上所有点为边强连通。由于题目要求连边最少,那么就使每次加的边让更多点边强连通。由上分析环的构造可知lca离两点越远对图上点贡献越多。那么每次将lca离两点最远的叶节点相连。本题不要求输出方案,所以因为每次都会
    消掉两叶节点那么答案直接为(叶节点数 + 1)/2。(奇数剩余的一个点随便向其他点连边)。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define lson x<<1
    #define rson x<<1|1
    #define ll long long
    #define rint register int
    #define mid ((st[x].l + st[x].r) >> 1)
    using namespace std;
    template <typename xxx> inline void read(xxx &x) {
    	char c = getchar(),f = 1;x = 0;
    	for(;c ^ '-' && !isdigit(c);c = getchar());
    	if(c == '-') c = getchar(),f = -1;
    	for(;isdigit(c);c = getchar()) x = (x<<1) + (x<<3) + (c ^ '0');
    	x *= f;
    }
    template<typename xxx> inline void print(xxx x)
    {
        if(x<0){putchar('-');x=-x;}
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    const int maxn = 200010;
    const int inf = 0x7fffffff;
    const int mod = 1e9 + 7;
    struct edge{
    	int to,last,fg,from;	
    }e[maxn];
    int head[maxn],tot;
    inline void add(int from,int to) {
    	++tot;
    	e[tot].to = to;
    	e[tot].from = from;
    	e[tot].last = head[from];
    	head[from] = tot;
    }
    int n,m;
    int dfn[maxn],low[maxn],cnt;
    inline void tarjan(int x,int in_edge) {
    	dfn[x] = low[x] = ++cnt;
    	for(rint i = head[x];i; i = e[i].last) {
    		if(!dfn[e[i].to]) {
    			tarjan(e[i].to,i);
    			if(low[e[i].to] < low[x]) low[x] = low[e[i].to];
    			if(low[e[i].to] > dfn[x]) {
    				e[i].fg = e[i^1].fg = 1;
    			}
    		}
    		else if(i ^ (in_edge ^ 1) && dfn[e[i].to] < low[x]) low[x] = dfn[e[i].to];
    	}
    }
    int col[maxn],num,in[maxn];
    inline void ddfs(int x) {
    	col[x] = num;
    	for(rint i = head[x];i;i = e[i].last) {
    		if(col[e[i].to] || e[i].fg) continue;
    		ddfs(e[i].to);
    	}
    }
    int main()
    {
    	read(n);read(m);tot = 1;
    	for(rint i = 1;i <= m; ++i) {
    		int x,y;
    		read(x);read(y);
    		add(x,y);add(y,x);
    	}	
    	for(rint i = 1;i <= n; ++i) {
    		if(!dfn[i]) {
    			tarjan(i,0);
    		}
    	}
    	for(rint i = 1;i <= n; ++i) {
    		if(!col[i]) {
    			++num;
    			ddfs(i);
    		}
    	}
    	for(rint i = 2;i <= tot; ++i) {
    		if(col[e[i].from] == col[e[i].to]) continue;
    		++in[col[e[i].from]];
    		++in[col[e[i].to]];
    	}
    	int ans = 0;
    	for(rint i = 1;i <= num; ++i) 
    		if(in[i] == 2) ++ans;
    	print((ans + 1) / 2);
    	return 0;
    }
    /*
    
    */
    
  • 相关阅读:
    697-数组的度
    1013-将数组分成和相等的三个部分
    linux命令基础
    Fiddler安卓抓包设置
    HTTPS协议
    HTTP协议
    TCP和UDP的区别
    TCP三次握手以及四次挥手
    OSI七层网络模型,数据传输过程解析
    jmeter常用命令(非GUI模式命令)
  • 原文地址:https://www.cnblogs.com/Thomastine/p/11783730.html
Copyright © 2020-2023  润新知