Kosaraju 笔记

news/2025/10/31 20:45:05/文章来源:https://www.cnblogs.com/CTHOOH/p/19181224

在做 ARC069F Flags 时看到有一个用 kosaraju 的 nb 做法,于是研究了一下 kosaraju。

Kosaraju 算法

kosaraju 算法是一种找出强连通分量的算法,用途和 tarjan 类似,但是代码更好写,并且在某些题上比 tarjan 算法有更多性质。

算法流程

  1. 对原图做一次 DFS:
    • 初始化一个栈 \(S\)
    • 对图中每个点未访问的点都 DFS 一遍,DFS 的时候像求 DFS 树一样,依次遍历 \(u\) 连接的没访问过的点 \(v\),并递归对 \(v\) 进行 DFS \(v\)。当 \(u\) 所有后继点都遍历完后,往栈 \(S\) 的栈顶加入 \(u\)
  2. 按照栈中的顺序,在反图上做 DFS:
    • 维护一个标记数组以记录每个点所属的 SCC 编号。
    • 按照栈 \(S\) 中从栈顶到栈底的顺序依次取出结点 \(u\),若 \(u\) 未被标记则在反图上 DFS,并把每个 \(u\) 能到达的后继点的 SCC 编号标记为相同的编号。
    • 按照这个逻辑,则每次在反图上的 DFS 都能找出一个完整的 SCC。

代码:

//adj 是原图,nadj 是反图。void dfs1(int x) {vis[x] = 1;for (auto y : adj[x]) {if (!vis[y]) {dfs1(y);}}stk.push(x);
}
void dfs2(int x) {scc[x] = cid;for (auto y : nadj[x]) {if (!scc[y]) {dfs2(y);}}
}
void kosaraju() {for (int i = 1; i <= n; ++i) {if (!vis[i]) {dfs1(i);}}while (!stk.empty()) {int u = stk.top();stk.pop();if (!scc[u]) {++cid;dfs2(u);}}
}

算法正确性证明

引理:记结点 \(u\) 在栈中的位置为 \(f_u\)(栈顶的 \(f\) 值为 \(n\),栈底的 \(f\) 值为 \(1\)),所属的强连通分量编号为 \(C_u\),那么对于原图的一条边 \(u \to v\),若满足 \(C_u \ne C_v\),则有 \(f_u > f_v\) 成立。

证明:可以把每条跨 SCC 的边 \(u \to v\) 分为:\(u, v\) 在同一个 DFS 中遍历到的,和在不同 DFS 中遍历到的。(DFS 指代码中的 dfs1

  • 对于被同一个 DFS 遍历到的,一定是先经过 \(u\) 再经过 \(v\),由于是后缀顺序遍历,所以越晚遍历到的点 \(f\) 值越小,故 \(f_u > f_v\) 成立。

  • 对于被不同 DFS 遍历到的,一定是先经过 \(v\) 再经过 \(u\),由于它们属于不同的 DFS 过程,先被 DFS 的 \(f\) 一定小,故 \(f_u > f_v\) 成立。

综上,原命题成立。

我们按照 \(f\) 从大到小在反图上 DFS,每次都只会遍历栈顶 \(u\) 所属强连通分量 \(C_u\) 中的点,因为 \(C_u\) 在 DAG 上的前驱点的 \(f\) 值都比 \(f_u\) 大,所以一定都在 \(u\) 之前被遍历过了。

可以注意到我们在根据拓扑序访问 DAG 上的强连通分量,所以求出的 SCC 编号从小到大排列就是 DAG 的拓扑序。(注意如果该算法用来实现 2-SAT,要与 Tarjan 算法的写法区分)

Flags

题意

给定 \(n\) 个二元组 \((x_i, y_i)\)\(n\) 个数轴上的点,第 \(i\) 个点可以取在 \(x_i\)\(y_i\),你需要求出一个方案使得 \(n\) 个点中相邻两个点的最小距离最大。

很明显可以先二分答案,然后转化成一个 2-SAT 问题,朴素的做法就是线段树优化建图 + Tarjan。

但是我们可以使用 Kosaraju,Kosaraju 在优化建图的问题上与 Tarjan 的区别就在于,Kosaraju 的形式很简洁,使得我们能够直接用数据结构优化 DFS 找点的过程,而不用像 Tarjan 一样建立虚点。通常情况下 Kosaraju 的时空常数都更好,部分情况下 Kosaraju 有复杂度更好的做法(有个点分树优化建图的题 Kosaraju 的复杂度更好,懒得放了)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/952205.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Manacher 代码贴贴

#include<bits/stdc++.h> using namespace std; const int N=1.1e7+5; char rS[N]; char S[N<<1]; int P[N<<1],n; void init(){n=strlen(rS);int k=0;S[k++]=$;S[k++]=#;for(int i=0;i<n;i++){S…

Python测试(上)_ 不存在不写bug的程序员

Python测试(上)_ 不存在不写bug的程序员#导入测试内置模块import unittest#导入要测试的apifrom _try_except import condition#需要继承unittest.TestCase,def函数必须用test_开头class MyTestCase(unittest.TestC…

P9119 [春季测试 2023] 圣诞树

首先需要发现一些性质,不然就真成不可做问题了,考虑凸多边形的一些性质。 考虑四边形定理,两条相交边长度一定大于两条不交边长度,这启示我们路径连线本质不交,然后我们继续思考路径形态。 路径形态是这样子的,你…

Java性能调优的艺术:从字节码到云端的全链路优化

在Java开发的世界里,我们常常享受其“一次编写,到处运行”的便利和垃圾回收带来的自动化管理。然而,当应用变得复杂、负载升高时,性能问题便会悄然而至:响应缓慢、吞吐量下降、频繁GC,甚至内存溢出导致服务崩溃。…

2025.10.31总结 - A

今天没课,在宿舍休息了,加油,依旧按时完成博客园,加油

用隐式马尔科夫模型检测XSS攻击Payload

其实XSS说白了,就是通过向网页中注入恶意的脚本代码,一般来说都是 JavaScript,让代码在其他用户的浏览器中执行,从而达到窃取信息、冒充身份、传播木马等目的。换句话说,网站本来应该只展示安全的内容的,但是攻击…

revit api创建文字注释

revit api创建文字注释public void MyFirstMacroAppCS() {Autodesk.Revit.DB.XYZ baseVec = Application.Create.NewXYZ(1.0, 0.0, 0.0);Autodesk.Revit.DB.XYZ upVec = Application.Create.NewXYZ(0.0, 0.0, 1.0);Aut…

mysql 查询今天、昨天、本周、上周、本月、上月、本季度、上季度、本年、上一年、的数据

1、今天select * from 表名 where to_days(时间字段名) = to_days(now())2、昨天select * from 表名 where to_days( now( ) ) - to_days( 时间字段名) <= 13、近7天select * from 表名 where date_sub(curdate(), …

P10674 [MX-S1-T3] 电动力学 题解

P10674 [MX-S1-T3] 电动力学 题解 提供一种比现有题解简单的 DP 方式和用到结论的证明。首先,建立原图的圆方树,注意到如果两个圆点 \(x,y\in T\),那么在圆方树上 \(x\to y\) 路径上的所有方点对应的点双连通分量中…

【UE引擎解构】- GamePlay篇 : 移动

基本组件(继承链): UActorCompoent - 负责组件的生命周期管理、激活/停用、与Actor的绑定等USceneCompoent - 具有变换并支持附件(组件依附),但没有渲染或碰撞功能。UPrimitiveCompoent - 具有渲染和物理信息,可以实…

读后感一:《代码大全 2》—— 从 “写代码” 到 “做工程” 的思维跃迁 - A

初读《代码大全 2》时,我正陷入 “代码能跑就行” 的认知误区 —— 总以为优化语法、缩减行数就是提升代码质量的全部,直到这本书用近千页的内容,彻底颠覆了我对软件开发的认知。它没有停留在 “if-else 怎么用”“…

Ai元人文:对“局限性”的反驳

Ai元人文:对“局限性”的反驳 有些人指出,有关Ai元人文构想的研究仍存在一定的局限性。首先,由于AI元人文理论仍在发展完善中,一些核心概念和机制还需要进一步的理论论证和实证检验。其次,该理论在跨文化应用方面…

读后感二:《代码大全 2》—— 穿越技术迭代的 “软件开发说明书” - A

在这个框架迭代比季节更替还快的时代,拿起《代码大全 2》这样一本初版于 2004 年的书,我起初带着 “会不会过时” 的疑虑。但读完才发现,这本书就像软件开发领域的 “经典物理学”—— 它不依赖特定语言或框架,而是…

JDBC练习

环境准备:数据库表 tb_brand 实体类 Brand 测试用例查询所有数据 package com.itheima.example;import com.itheima.pojo.Brand; import org.junit.Test;import java.sql.*; import java.util.ArrayList;/*** 品牌数据…

2-SAT学习笔记

问题 给出一些\(0/1\)选择和一些约束条件,求出一组满足所有条件的解为方便叙述,我们把第 \(i\) 个 \(0/1\) 选择表示为 \(ai,0\), \(ai,1\) 首先观察性质,\(ax,t,ax,t\) 冲突意味着两者只能选其一。有了这个性质,我…

打造自己的 Claude Code:LangGraph + MCP 搭建一个极简的 AI 编码助手

实践是最好的学习方式。为了深入理解 LangGraph 和模型上下文协议(MCP)服务器的生态,我们来从零开始构建一个 CLI 编码代理。我们的目标是,抛开 Claude Code 那些花里胡哨的功能,看看最基础的编码代理能做到什么程…

CSP-S 2023-2024 分析

CSP-S 2023-2024 分析前言 考前临时写的题解,希望会有帮助 2023 比赛回顾 分数是 100+50+0+0=150 比赛打的没什么问题,就是当时水平菜,写不出 DP 和大模拟。 密码锁 枚举每一个密码是否可能。 消消乐 记 \(f_i\) 为…

Java:历久弥坚的编程基石,在变革中永葆活力

在技术浪潮日新月异的今天,编程语言的江湖风起云涌,新贵辈出,各领风骚数年。然而,有一门语言,自1995年诞生以来,便以其坚如磐石的稳定性、跨平台的包容性和蓬勃发展的生态系统,始终屹立于行业的核心地带,它就是…

revit api获取与连接器connector连接的图元

revit api获取与连接器connector连接的图元public void GetElementAtConnector(Connector connector) {MEPSystem mepSystem = connector.MEPSystem;if (null != mepSystem){string message = "Connector is owne…

Ant构建项目 - 沐春风

前言: Ant是java世界里第一个具有里程碑意义的项目构建工具。 官网地址:http://ant.apache.org/。 文档地址(Ant task):http://ant.apache.org/manual/index.html。 扩展工具地址(比如checkstyle和clover):http://…