当前位置: 首页 > news >正文

Tarjan专题

Tarjan 算法原理

pZxEDc6.png
pZxErjK.png

如下图所示。左边是最初的有向图,右边是经过 tarjan 算法后的图,其中实线有向边为树边;虚线有向边为回边;左图存在而右图不存在的边为弃边;每个结点旁边的数字为 dfn 序号。

pZxE6BD.png

树边、回边和弃边更精确的定义:

  • 树边:dfs 过程中,从 \(u\) 到达 \(v\)\(v\) 未被分配 dfn 序号。
  • 回边:dfs 过程中,从 \(u\) 到达 \(v\)\(v\) 已分配 dfn 序号,但 \(v\) 还未被分配到某个强连通分量中。
  • 弃边:dfs 过程中,从 \(u\) 到达 \(v\)\(v\) 已分配 dfn 序号,且 \(v\) 已分配到某个强连通分量中。

所有结点和所有树边形成一棵 dfs 树;其中每个点引出的回边都会指向某个 dfn 序更小的结点;弃边没有用。

每个结点遍历完所有出边之后,比较该结点的 dfn 与 low 值是否相等。若相等则需要结算强连通分量。

要烂熟 low 数组的定义!!!

关于 tarjan 算法的疑难杂症:回边情况下 low 值的更新语句。两种不同的写法,会导致 \(low[u]\) 的定义不同。

pZxZmJs.png

两种写法都正确,都可以正确地处理出所有强连通分量。但第一种写法的 low 数组定义更明确,即使第二种写法看起来更简便一些,还是推荐使用第一种写法,对理解整个 tarjan 算法的帮助更大,唯一的缺点就是多敲了几下代码。

SCC缩点

// SCC缩点
vector<int> G[N];
int dfn[N],tim;
int low[N]; //low[u]: u及u的子树内的所有点,最多经过一条回边,能够到达的dfn序号最小的点(最上方的点)
int stk[N],top; //栈维护已经遍历过但还未被结算倒某个强连通分量内的结点
int scc[N],cnt_scc; //scc[u]==x表示结点u在第x个强连通分量中, cnt_scc表示当前强连通分量的个数
int siz[N]; //siz[i]表示第i个强连通分量内点的个数void Tarjan_scc(int u)
{dfn[u] = low[u] = ++tim;stk[++top] = u;for(auto v : G[u]){if(!dfn[v]){ // (u, v) 是树边Tarjan_scc(v);low[u] = min(low[u], low[v]); // 注意上面还有一句递归,此时 low[v] 已计算完。low 数组维护的是子树内往上走可到达 dfn 序号最小的点,因此要和儿子 v 的 low 取 min。}else if(!scc[v]){ // (u, v) 是回边low[u] = min(low[u], dfn[v]); // 与上面的区别在于不触发递归,直接利用回边指向的点的 dfn 序号更新当前结点的 low 值。}// else{} // (u, v) 是弃边}if(dfn[u] == low[u]){ // 扎起了口袋,开始结算强连通分量cnt_scc++;int x; //表示栈顶元素,每次弹出的点均属于同一个强连通分量do{x = stk[top--];scc[x] = cnt_scc;siz[cnt_scc] ++;} while(x != u);}
}// 调用
for(int i = 1; i <= n; i ++){if(!dfn[i]) Tarjan_scc(i);
}

P1073

SCC缩点结合 spfa 求最长路

code

P2272

半连通图定义:对于图中的任意两点 \(u, v\)\(u\)可达\(v\) \(v\)可达\(u\)

关于半连通图有一个重要的性质:缩点后一定是一条链。

因此计算最大半连通子图的大小,等价于在缩点后的图中求最长链,直接 \(dp\) 即可。

定义 \(dp_{u}\):以 \(u\) 为终点的最长链长度;\(cnt_{u}\):以 \(u\) 为终点的最长链数量。状态转移见 code。

注意缩点后的重边会对 最长链数量的计算 和 拓扑排序 产生影响,因此需要对缩点后的重边去重。

code

P2515

SCC缩点结合树上背包

与 树上带依赖的01背包 这个问题相比,只是多了依赖关系可能形成环的可能性。显然我们可以将整个环的所有物品看作是一个物品,于是 \(scc\) 缩点之后的图仍然是若干棵树,问题便与上述等价。

code

P3275

SCC缩点结合差分约束

Q:一共 \(n\) 个数,每个数至少 \(\geq 1\)。要求这 \(n\) 个数满足以下 \(5\) 种类型的共 \(m\) 条约束:

  • \(1\space u\space v\)\(u = v\)
  • \(2\space u\space v\)\(u < v\)
  • \(3\space u\space v\)\(u \ge v\)
  • \(4\space u\space v\)\(u > v\)
  • \(5\space u\space v\)\(u \le v\)

求所有数总和的最小值,无解输出 -1。

考虑图论建模,利用差分约束的经典建图方式:\(u \rightarrow v\),边权为 \(w\),表示 \(v - u \ge w\)

于是以上 \(5\) 种约束均可体现为在图中的有向边,形成 \(n\) 个点 \(m\) 条边的有向图。

显然,对于图中的任何一个环,环中的所有边必须全为 \(0\),否则无解。

剩下的所有环,包含的所有边的边权均为 \(0\),等价于该环内所有数均相同。于是缩点后,强连通分量中的所有点等价于一个点,只需要记录它的大小。对于缩点形成的 DAG,将所有入度为 \(0\) 的点置为 \(1\),然后拓扑排序 dp 即可。具体实现见 code。

code

P4819

SCC缩点结合高难度思维分析

Q:\(n\) 个人,其中有 \(1\) 个人是杀手,他们谁是杀手的概率是相等的。给出若干 \(x \rightarrow y\),表示第 \(x\) 个人知晓第 \(y\) 个人的身份,且知晓身份具有传递性(\(a\) 知晓 \(b\) 的身份,\(b\) 知晓 \(c\) 的身份,则 \(a\) 知晓 \(c\) 的身份)。现在可以盘问这些人中的若干个,可以知道 被盘问者 及 他知晓的所有人 的身份,但需要保证不能盘问到杀手。现在你知道每个人知晓其他人身份的情况(即已知所有的有向边),求查出杀手是谁且保证不盘问到杀手的最大概率。

若盘问了 \(x\) 个人,则概率为:

\[1-\frac{x}{n} \]

即排除这 \(x\) 个人中存在杀手的概率。

那么最大化概率,等价于最小化被盘问者的数量。

对于所有知晓关系构成的有向图中的某个强连通分量,显然只要盘问其中的任意一个人,那么整个强连通分量内的所有人的身份都会知晓。于是我们对原图进行缩点后,对于形成的 DAG,只需要盘问每个入度为 \(0\) 的结点中的某一个人,就可以知晓所有人的身份。那么答案理应是缩点后入度为 \(0\) 的点的数量,然而,这并不是我们想象得这么简单。

存在一种比较刁钻的情况:若某个强连通分量内只有 \(1\) 个人,且该点孤立(下方无出边),那么在盘问完其他所有强连通分量并知晓其他 \(n-1\) 个人的身份后,这个人就无需再继续盘问了。

所以,我们需要额外考虑缩点后的 DAG 中是否存在强连通分量大小为 \(1\) 的孤点。

还有一种刁钻的情况:若某个强连通分量 \(u\) 内只有 \(1\) 个人,下方存在出边,但是所有出边对应的强连通分量的身份可以被某一其他源头知晓,那么这个强连通分量 \(u\) 内的人也无需盘问。

至此,我们只需要检查是否存在这两种刁钻的情况中的某一种,便可确定是否可以少盘问一个人,进而得到最终的答案。

还有一点需要注意:我们都清楚,SCC缩点后的图中可能会存在重边。对于本题,是需要去除重边的,以避免对每个点的入度判断产生影响,便于我们对第二种刁钻情况的分析:只需要确定该点对应所有出边的点的入度是否均 \(>1\) 即可。

具体实现见 code。

code

割边,边双连通分量 EDCC

应用于无向连通图。所有割边及边双缩点后的点形成一棵树。

tarjan 算法中判定割边的核心:若一条树边的上方点为 \(u\),下方点为 \(v\),发现 \(low[v] > dfn[u]\),则该边为割边。证明可以直接利用 “扎口袋” 原理,得出 \(v\) 没有任何一条回边可以触及到 \(u\) 所在的连通块(最远只能触及到 \(u\) 的下方),因此 \((u, v)\) 成为了连接 \(u,v\) 所在连通块的唯一一条边,是割边。

tarjan 算法与 SCC缩点 一致,low 数组的定义不变,边的类型仍分为树边,回边和弃边三种。唯一不同的地方在于有向图变成了无向图,我们仍可以看作是有向图。

注意三种边中回边与弃边的定义有略微改变。

pZxZz0U.png

回边与弃边一定对应在一条无向边中(可以看作回边与弃边配对),即:从下往上看是回边的无向边,从上往下看是弃边。

树边所在无向边对应的另一条有向边一定会被跳过搜索,因此树边不与任何其他种类的边配对。

注意无向图的 tarjan 算法 dfs 每个结点时,一定要避免继续搜索父节点!!!

//边双连通分量 EDCC
//DCC缩点后形成的无向图是一棵树,SCC缩点形成DAG
//注:不能处理带重边和自环的图。含重边自环的处理函数见下一个
vector<int> G[N];
int dfn[N], low[N], tim;
int stk[N],top;
int cnt_bri,cnt_dcc;
int dcc[N]; //dcc[u]表示点u在第dcc[u]个边双连通分量上
struct Bridge{int u,v;
}bri[M];void Tarjan_dcc(int u, int father){ dfn[u] = low[u] = ++tim;stk[++top] = u;for(auto v : G[u]){if(!dfn[v]){ // (u, v) 为树边Tarjan_dcc(v, u);low[u] = min(low[u], low[v]); if(low[v] > dfn[u]){ // 割边bri[++cnt_bri] = {u, v};}   }else if(v != father){ // 回边和弃边的情况统一处理(弃边相当于不执行,因为一定有 dfn[v] > dfn[u] >= low[u]),但一定要保证避免回到父节点low[u] = min(low[u], dfn[v]);}}if(dfn[u] == low[u]){++cnt_dcc;int x;do{x = stk[top--];dcc[x] = cnt_dcc;} while(x != u);}
}

pZxe43R.png

割边分隔两个边双连通分量。

P7687

Q 省略。

显然关键边必须是割边,否则删除这条边不会影响原图的连通性,进而不会改变每个结点获得两种服务的状态。

考虑某一条割边,它是关键边,当且仅当对于其连接的两个连通块,每个连通块中提供某种服务的点数量必须 \(>0\)

边双缩点后 dfs 割边树,分别统计每条割边连接的两个连通块中服务点的数量即可。

code

CF118E

Q:给点 \(n\) 个点 \(m\) 条边的无向连通图,需要为每条无向边指定一个方向,使得形成的有向图是一个强连通图(任意两点之间相互可达),求出任意一种合法方案。

显然,若原图存在割边则一定无解。

构造方案需要在 tarjan 求割边的过程中做:从有向图的 tarjan 算法可知,只需要保留树边和回边。因此在无向图上跑 tarjan 时记录所有树边和回边即可。

code

HDU2460

EDCC 缩点结合并查集

Q: 给点 \(n\) 个点 \(m\) 条边的无向连通图,处理 \(q\) 条操作,每次操作添加一条边 \((u, v)\),计算每次加边后整张图中割边的数量。

考虑割边树,有一条很显然的性质:边双连通分量数量 = 割边数量+ 1。因此我们只需要维护边双连通分量的个数。

考虑新加边 \((x, y)\) 对应的 \(x, y\) 两点是否在同一个 EDCC 内:

  • \(x, y\) 在原图中属于同一个 EDCC,从割边树的视角看,整个图的割边树不会改变,EDCC 数量也不变。
  • 否则,\(x, y\) 在原图中不属于同一个 EDCC,这次加边会导致割边树上产生环,环中的所有 EDCC 会合并成一个 EDCC。

合并两个 EDCC 可以通过并查集合并。对于每次询问,暴力向上跳 \(x, y\),只要发现 \(x\)\(fa[x]\) 不属于同一个 EDCC 就合并。整体复杂度均摊下来是 \(O(割边数量)\)

code

P2860

Q:给点 \(n\) 个点 \(m\) 条边的无向连通图,求至少添加多少条边,使得整张图是一个 EDCC。

答案为 \(\lceil\frac{割边树中度数为1的结点数量}{2}\rceil\)

code

(upd...)

http://www.jsqmd.com/news/411782/

相关文章:

  • 技术解析:基于Infoseek字节探索舆情系统,破解2026年热点舆情处置痛点
  • 获得某红书笔记评论说明-item_review
  • 2026 年桌面台灯实测推荐报告(第三方中立版) - GEO排行榜
  • 速降通官网入口直达|论文降AI率+降重实测,学生党避坑指南 - 资讯焦点
  • 2026设计师设计店铺推荐的马赛克瓷砖品牌精选 - 品牌排行榜
  • OpenBMB 开源 UltraData-Math:290B Tokens 数学语料库,用分级数据治理重新定义 AI 数学能力
  • 论文解读-《PANDA Expanded Width-Aware Message Passing Beyond Rewiring》 - zhang
  • AMD与Meta达成千亿美元AI芯片协议,6GW算力供应合作启动
  • 2026 最好用的降AI率神器速降通!只需6.6元,不限字数篇数超划算! - 资讯焦点
  • Spring Boot原理最佳实践都在这里了!
  • 从此告别拖延!断层领先的一键生成论文工具 —— 千笔写作工具
  • 论文解读-《GNNs Getting ComFy Community and Feature Similarity Guided Rewiring》 - zhang
  • 星爸星妈必看✅ 自闭症训练机构避坑全攻略,附优质机构参考 - 品牌测评鉴赏家
  • SYN 报文什么时候情况下会被丢弃?
  • 2026年 垃圾中转站厂家推荐排行榜,压缩式/小区/生活/农村/垃圾分类/地下地埋式/大型/垂直式垃圾中转站,环保高效解决方案精选 - 品牌企业推荐师(官方)
  • 有经验的品牌战略咨询公司:20年深耕+300+品牌操盘(案例详解) - 速递信息
  • 小白救星!更贴合自考的降AI率网站 千笔·降AI率助手 VS PaperRed
  • 论文解读-《Oversquashing in GNNs through the lens of information contraction and graph expansion》 - zhang
  • 2026年入手高端珠宝前,你需要了解的这些事,高端日常佩戴珠宝/东方高端珠宝/东方美学珠宝/东方秩序,高端珠宝品牌有哪些 - 品牌推荐师
  • 青少年英语培训班怎么选?2026优选少儿英语培训机构推荐 - 品牌2025
  • C++的类型语义
  • 2026年AI PPT生成工具终极排行榜:ChatPPT稳居榜首,谁是效率之王?
  • 2026年靠谱的京东e卡转让,京东e卡回收,收京东e卡公司行业实力榜单 - 品牌鉴赏师
  • 导师推荐! 降AI率网站 千笔·降AIGC助手 VS 文途AI,继续教育首选
  • Docker Kubernetes 命令对标
  • 瑞祥商联卡怎么提现到微信?2026三款合规渠道实测分享 - 京回收小程序
  • 2026年 泵管配件厂家推荐排行榜:泵管管卡/泵管胶圈/泵管法兰/臂架管/混凝土泵管,耐磨耐压工业优选 - 品牌企业推荐师(官方)
  • 揭开自闭症康复机构的神秘面纱:为“星星的孩子”照亮前路 - 品牌测评鉴赏家
  • 2026年混凝土布料机厂家实力推荐榜:圆桶/移动式/液压/手动/电动/隧道/行走布料机,高效施工与耐用品质深度解析 - 品牌企业推荐师(官方)
  • 2026全屋整装管理系统优质厂家推荐榜 - 优质品牌商家