zoukankan      html  css  js  c++  java
  • Tarjan 割点学习笔记

    在一个无向图中,如果删掉点 $v$ 后图的连通块数量增加,则称点 $v$ 为图的割点

    Tarjan 算法可以在线性时间求无向图的所有割点。

    定义

    $mathrm{dfn}(u)$ 表示进入节点 $u$ 时的时间(时间截)。

    $mathrm{low}(u)$ 表示由节点 $u$ 开始搜索所能到达的点中,在搜索树上是 $u$ 的祖先且 $mathrm{dfn}$ 最小的节点的 $mathrm{dfn}$。

    描述

    类似于 Tarjan 求强连通分量,大致流程:

    1. 选定一个节点作为根节点,开始 DFS;

    2. 初始化当前点的 $mathrm{dfn}$ 和 $mathrm{low}$ 均为当前时间截;

    3. 遍历当前点 $v$ 的所有邻接点(与 $v$ 相连的点);

    4. 如果某个邻接点 $u$ 在 DFS 树中,更新 $mathrm{low}(v) = min(mathrm{low}(v), mathrm{dfn}(u))$;

    5. 如果某个邻接点 $u$ 不在 DFS 树中,则对 $u$ 进行 DFS,完成后更新 $mathrm{low}(v) = min(mathrm{low}(v), mathrm{low}(u))$;

    6. 对于根节点,如果它在 DFS 树上有两个或更多的子节点,则它为割点;

    7. 对于一个 DFS 树上的非根节点 $u$,如果存在子节点 $v$,满足 $mathrm{low}(v) geq mathrm{dfn}(u)$,则 $u$ 为割点。

    解释

    如果某个邻接点 $u$ 在 DFS 树中,更新 $mathrm{low}(v) = min(mathrm{low}(v), mathrm{dfn}(u))$;

    $mathrm{low}$ 被定义为节点经过最多一条后向边(与 DFS 方向相反,从某个节点指向其某个祖先的边)能到达最远的祖先。如果 $mathrm{dfn}(u)$ 改为 $mathrm{low}(u)$,因为 $u$ 已经在 DFS 树中,那么 $mathrm{low}(v)$ 为经过两条后向边到达节点的 $mathrm{dfn}$,与定义不符。

    对于根节点,如果它在 DFS 树上有两个或更多的子节点,则它为割点;

    显然,根是两棵子树上节点的唯一连通方式,删掉后破坏原图的连通性。

    对于一个 DFS 树上的非根节点 $u$,如果存在子节点 $v$,满足 $mathrm{low}(v) geq mathrm{dfn}(u)$,则 $u$ 为割点。

    根据定义,$v$ 向上无法到达 $u$ 的父节点。如果 $mathrm{low}(v) = mathrm{dfn}(u)$ 即以 $v$ 为根的子树最远到达 $u$,删掉后子树下的点无法到达更远的祖先,故判读时可以取等。

    模板

    Luogu 3388

    #include <cstdio>
    
    const int SIZE = 100005;
    
    int cut[SIZE], cutTot, root;
    int low[SIZE], dfn[SIZE], time;
    int h[SIZE], to[SIZE << 1], nxt[SIZE << 1], tot;
    
    int min(int x, int y) {
    	return x < y ? x : y;
    }
    
    void add(int x, int y) {
    	to[++tot] = y;
    	nxt[tot] = h[x];
    	h[x] = tot;
    }
    
    void tarjan(int x) {
    	low[x] = dfn[x] = ++time;
    	int cnt = 0;
    	for (int i = h[x]; i; i = nxt[i]) {
    		int y = to[i];
    		if (!dfn[y]) {
    			cnt++;
    			tarjan(y);
    			low[x] = min(low[x], low[y]);
    			if (x == root && cnt >= 2) {
                    // 判断是否被计数过,下同
    				if (!cut[x]) cut[x] = ++cutTot;
    			} else if (x != root && dfn[x] <= low[y]) {
    				if (!cut[x]) cut[x] = ++cutTot;
    			}
    		}
    		else low[x] = min(low[x], dfn[y]);
    	}
    }
    
    int main() {
    	int n, m;
    	scanf("%d %d", &n, &m);
    	for (int i = 1; i <= m; i++) {
    		int x, y;
    		scanf("%d %d", &x, &y);
    		add(x, y);
    		add(y, x);
    	}
    	for (int i = 1; i <= n; i++) if (!dfn[i]) root = i, tarjan(i);
    
    	printf("%d
    ", cutTot);
    	for (int i = 1; i <= n; i++) if (cut[i]) printf("%d ",i);
    	return 0;
    }
    

    参考资料

  • 相关阅读:
    deepin v20装机后python配置
    lotus命令详解
    Lotus 客户端命令集合
    在Windows 7或Server 2008 R2上安装更新时提示0x80092004错误的解决方案
    Delphi:如何将列表作为参数传递给SQL查询?
    delphi Ado 执行带有冒号字符语句的处理。
    SQLite与Delphi XE4 (一)
    delphi在TMemo中实现高亮文字
    Delphi中用beep函数拼出节奏和歌曲
    在oracle中插入数据报错:ORA-00984列在此处不允许
  • 原文地址:https://www.cnblogs.com/lcfsih/p/14391388.html
Copyright © 2011-2022 走看看