首页 > 其他分享 >Tarjan 全家桶

Tarjan 全家桶

时间:2024-02-21 21:44:54浏览次数:22  
标签:Tarjan int 全家 连通 dfn low operatorname 分量

部分定义摘自 Alex_Wei 的博客,如有侵权,请联系笔者删除。

割点

在无向图中,删去后使得连通分量数增加的结点称为割点

孤立点不是割点。

非根节点 \(u\) 为割点当且仅当 dfs 树上存在一条树边 \(u \rightarrow v\) 使得 \(\operatorname{low}_v \geq \operatorname{dfn}_u\)。

根节点 \(u\) 为割点当且仅当 dfs 树上 \(u\) 有两个以上的子节点。

void tarjan(int u, int last, bool isRoot) {
	dfn[u] = low[u] = ++timer;

	int son = 0;
	for (int i = head[u]; ~i; i = E[i].nxt) {
		if (i == (last ^ 1)) continue;
        
        int v = E[i].v;
		if (!dfn[v]) {
            son++; tarjan(v, i, false);
			low[u] = min(low[u], low[v]);
            
			if (low[v] >= dfn[u] && !isRoot) isCut[u] = true;
		}
		else low[u] = min(low[u], dfn[v]);
	}

	if (isRoot && son >= 2) isCut[u] = true;
}

点双连通分量

点双连通图:不存在割点的无向连通图称为点双连通图,孤立点和孤立边均为点双连通图。

点双连通分量:一张图的极大点双连通子图称为点双连通分量(V-BCC),简称点双

性质

  1. 两个点双至多存在一个公共点(该点一定是割点)。
  2. 任意一个非割点的点都只存在于一个点双中,割点一定属于两个及以上的点双。
  3. 对于一个点双(除去孤立边),其内部任意两不同点一定存在两条及以上的点不重复的路径。

实现

我们需要在 Tarjan 算法的过程中维护一个栈,并按照如下方法维护栈中的元素:

  • 当一个节点第一次被访问时,将该节点入栈。
  • 当遍历到一条树边 \(u \rightarrow v\) 满足 \(\operatorname{low}_v \geq \operatorname{dfn}_u\) 时(此时 \(u\) 为割点),不断弹出栈内元素直至节点 \(v\) 被弹出,所有被弹出的节点(以及节点 \(u\))即为一个新的点双连通分量。

注意在弹栈的时候不能把节点 \(u\) 弹出,因为 \(u\) 可能属于多个点双。

void tarjan(int u, int last, bool isRoot) {
	dfn[u] = low[u] = ++timer;
	s.push(u);

	int son = 0;
	for (int i = head[u]; ~i; i = E[i].nxt) {
        if (i == (last ^ 1)) continue;
        
		int v = E[i].v;
		if (!dfn[v]) {
			son++; tarjan(v, i, false);
			low[u] = min(low[u], low[v]);

			if (low[v] >= dfn[u]) {
				vBCC_cnt++;

				int p = 0;
				do {
					p = s.top(); s.pop();
					vBCC[vBCC_cnt].push_back(p);
				} while (p != v);

				vBCC[vBCC_cnt].push_back(u);
			}
		}
		else low[u] = min(low[u], dfn[v]);
	}

	if (isRoot && !son) { // 特判孤立点
		vBCC_cnt++;
		vBCC[vBCC_cnt].push_back(u);
	}
}

割边

在无向图中,删去后使得连通分量数增加的边称为割边,也称

孤立边割边。

一条边 \(u \rightarrow v\) 是割边当且仅当:

  • \(u \rightarrow v\) 是 dfs 树的树边。
  • \(\operatorname{low}_v \gt \operatorname{dfn}_u\)。
void tarjan(int u, int last) {
	dfn[u] = low[u] = ++timer;

	for (int i = head[u]; ~i; i = E[i].nxt) {
        if (i == (last ^ 1)) continue;
        
		int v = E[i].v;

		if (!dfn[v]) {
			tarjan(v, i);
			low[u] = min(low[u], low[v]);
            
			if(low[v] > dfn[u]) isBridge[i] = isBridge[i ^ 1] = true;
		}
		else low[u] = min(low[u], dfn[v]);
	}
}

边双连通分量

边双连通图:不存在割边的无向连通图称为边双连通图。孤立点是边双连通图,但孤立边不是。

边双连通分量:一张图的极大边双连通子图称为边双连通分量(E-BCC),简称边双

实现

删去所有割边后,剩下的连通分量即为边双连通分量。

void dfs(int u) {
	vis[u] = true;
	eBCC[eBCC_cnt].push_back(u);

	for (int i = head[u]; ~i; i = E[i].nxt) {
		if (isBridge[i]) continue;

		int v = E[i].v;
		if (!vis[v]) dfs(v);
	}
}

强连通分量

定义一个强连通分量的根为该强连通分量中最浅的节点。

那么节点 \(u\) 为它所在强连通分量的根当且仅当 \(\operatorname{dfn}_u = \operatorname{low}_u\)。

实现

同样需要维护一个栈,并按照如下方法维护栈中的元素:

  • 当一个节点第一次被访问时,将该节点入栈。
  • 当点 \(u\) 满足 \(\operatorname{dfn}_u = \operatorname{low}_u\) 时,不断弹出栈内元素直至 \(u\) 被弹出,所有被弹出的节点即为一个强连通分量。

注意如果遍历到一个已经访问过的点 \(v\),只有在栈内的 \(v\) 才能用来更新 \(\operatorname{low}_u\)。

void tarjan(int u) {
	dfn[u] = low[u] = ++timer;
	s.push(u); inStack[u] = true;

	for (int i = head[u]; ~i; i = E[i].nxt) {
		int v = E[i].v;

		if (!dfn[v]) {
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (inStack[v]) low[u] = min(low[u], dfn[v]);
	}

	if (dfn[u] == low[u]) {
		SCC_cnt++;
		while (s.top() != u) {
			SCC[SCC_cnt].push_back(s.top());
			inStack[s.top()] = false; s.pop();
		}

		SCC[SCC_cnt].push_back(u);
		inStack[u] = false; s.pop();
	}
}

标签:Tarjan,int,全家,连通,dfn,low,operatorname,分量
From: https://www.cnblogs.com/xhgua/p/-/tarjan

相关文章

  • 【模板】多项式全家桶(多项式初等函数(部分))
    【模板】多项式初等函数同时作为https://github.com/caijianhong/template-poly的document。杂项数域为\(\mathbbF_{998244353}\),所以定义了mint为modint<998244353>。poly是多项式的类型,从std::vector<mint>继承而来。poly的构造函数如下:poly();explicitpoly(......
  • poly 全家福
    从\(\texttt{{\color{black}S}{\color{red}intilla}}\)老师那里蒯来的,改了马蜂。constintP=998244353,Pi=499122177;inlineintinc(inta,intb){return(a+=b)>=P?a-P:a;}inlineintdec(inta,intb){return(a-=b)<0?a+P:a;}inlineintmul(inta,intb){ints=1ll......
  • Tarjan 算法
    part1:离线求\(lca\)将走过的点且未回溯的标记为\(1\),已回溯的标记为\(2\),未访问标记为\(0\)把对于一点\(x\)的询问\(y\)存进相应的\(vector\),当回溯时判断如果询问的点标记为\(2\),则\(lca\)为询问的点\(y\)到根的路径上最早标记为\(1\)的点但直接找复杂度不对......
  • 通义千问上线春节新应用,AI帮你免费拍全家福
    2月5日,春节将至年味渐浓,阿里云通义千问APP上线多项免费新应用,涵盖全家福、拜新年、万物成龙等图像生成的新玩法,共提供超300套照片模板,用户上传照片即可生成全家福、团圆照、拜年照、千里江山主题照;此外,一个月前火爆全网的全民舞王应用也迎来上新,用户可通过一张照片生成拜年视频,用更......
  • Dijkstra/Tarjan-关键边
    题目描述总统要回家,即从s走到t,会经过一些街道,每条街道都是单向的并且拥有权值。现在,为了让总统更好的回家,要对每一条街道进行操作:①如果该街道一定在最短路上,则输出“YES”。②如果该街道修理过后,该边所在的最短路可以取代原先的最短路,则输出“CANx”,x是修改该街道的最小花......
  • Adobe2024最新版全家桶Ps、Pr、Ai、Ae、Au、Lrc、An直装版下载
    Adobe全家桶是Adobe公司开发的一套包括多个专业创意软件的综合性套装,涵盖了图形设计、影片制作、页面排版、音频编辑等多个创意领域。其中主要应用程序包括:Adobe2024全家桶Windows&Mac官方直装版AdobePhotoshop:图像处理和编辑软件,广泛用于照片修饰、合成和数字绘画。Ado......
  • Adobe 2024 全家桶 Windows&Mac 官方直装版
    简化了安装流程适用于小白,无脑直接安装。Adobe公司开发了许多专业的图形设计、影像处理、视频编辑、网页设计等领域的软件。以下是Adobe系列中一些常见的软件:AdobePhotoshop-用于图像编辑和处理的专业软件。AdobeIllustrator-用于创建矢量图形和插图的矢量图形编辑软件。A......
  • 计算几何全家桶
    完整板子#include<bits/stdc++.h>usingnamespacestd;constdoubleeps=1e-8,Pi=acos(-1.0);inlineintdcmp(doublex){return(x<-eps)?-1:(x>eps?1:0);}inlinedoubleAbs(doublex){returnx*dcmp(x);};namespaceFlat_Space{ structpt{ double......
  • tarjan 全家桶
    无向图的tarjan求边双连通分量的个数以及每个边双连通分量的大小以及每个边双连通分量包含哪些点。bridge数组表示一条边是不是桥。#include<bits/stdc++.h>usingnamespacestd;constintN=5e5+10,M=2e6+10;structedge{ intx,y,pre;}a[M*2];intalen,last[N];voidin......
  • Tarjan
    Tarjan前置知识搜索树&搜索森林\(在一张连通图中,所有的节点以及发生递归的边共同构成一棵搜索树。如果这张图不连通,则构成搜索森林。\)\(\color{green}树边:\)\(dfs经过的边,dfs搜索树の边\)\(\color{yellow}返祖边:\)\(可以理解为返回去の边\)\(\color{red}横叉边:\)\(df......