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

求LCA(倍增/DFS序/重链剖分)Go语言

本博客用于记录板子

  • 1、倍增算法
  • 2、DFS序求LCA
  • 3、重链剖分

学习算法推荐: 灵茶山艾府

1、倍增算法

O ( n log ⁡ n ) O(n \log n) O(nlogn)预处理, O ( log ⁡ n ) O(\log n) O(logn)查询

讲解看:树的第K个祖先

练习题:P3379 【模板】最近公共祖先(LCA)
直接贴下面代码会超时,这与bufio有关,换成快读就过了

代码:

package mainimport ("bufio". "fmt""math/bits""os"
)func main() {in, out := bufio.NewReader(os.Stdin), bufio.NewWriter(os.Stdout)defer out.Flush()var n, m, s, x, y intFscan(in, &n, &m, &s)g := make([][]int, n+1) // 数据量大的时候,bufio读取数据有问题,这里最好写成5e5+1for i := 0; i < n-1; i++ {Fscan(in, &x, &y)g[x] = append(g[x], y)g[y] = append(g[y], x)}mx := bits.Len(uint(n))pa := make([][]int, n+1)for i := range pa {pa[i] = make([]int, mx)for j := range pa[i] {pa[i][j] = -1}}dep := make([]int, n+1)var dfs func(int, int)dfs = func(u, p int) {dep[u] = dep[p] + 1pa[u][0] = pfor _, ne := range g[u] {if ne == p {continue}dfs(ne, u)}}dfs(s, 0)for i := 0; i < mx-1; i++ {for x := 1; x <= n; x++ {if p := pa[x][i]; p >= 0 {pa[x][i+1] = pa[p][i]} else {pa[x][i+1] = -1}}}uptoDep := func(x, d int) int {for k := uint(dep[x] - d); k > 0; k &= k - 1 {x = pa[x][bits.TrailingZeros(k)]}return x}lca := func(x, y int) int {if dep[x] < dep[y] {x, y = y, x}x = uptoDep(x, dep[y])if x == y {return x}for i := mx - 1; i >= 0; i-- {if pa[x][i] != pa[y][i] {x, y = pa[x][i], pa[y][i]}}return pa[x][0]}for i := 0; i < m; i++ {Fscan(in, &x, &y)Fprintln(out, lca(x, y))}
}

在这里插入图片描述

2、DFS序求LCA

为什么用DFS序求LCA?
优点: O ( n log ⁡ n ) O(n \log n) O(nlogn)预处理, O ( 1 ) O(1) O(1)查询,适用于大量查询的情况

讲解看 冷门科技 —— DFS 序求 LCA

核心思想:

dfs序是按照深度优先遍历得到的序列,dfn[x]是节点x在dfs序中的下标。

对于成祖辈节点的结构来说,祖宗节点的dfn要小于子节点的dfn

对于两个节点x, y

1、如果二者形成下述格式 (旁边数字是dfn)

在这里插入图片描述

这种情况下X一定不是LCA,且dfn值在 [ d f n [ x ] , d f n [ y ] ] [dfn[x], dfn[y]] [dfn[x],dfn[y]]的节点一定在D的子树中。
考虑Y分支上D 的第一个儿子节点Z, Z的父亲节点一定是X, YLCA

进一步的, [ d f n [ x ] + 1 , d f n [ y ] ] [dfn[x] + 1, dfn[y]] [dfn[x]+1,dfn[y]]深度最小的节点的父节点一定是LCA

2、如果二者形成下述格式
在这里插入图片描述
此时X一定是X,Y的LCA, 进一步的讲, [ d f n [ x ] + 1 , d f n [ y ] ] [dfn[x] + 1, dfn[y]] [dfn[x]+1,dfn[y]]深度最小的节点的父节点一定是LCA

没有上述两个情况之外的例子。

那么问题变成了求 [ d f n [ x ] + 1 , d f n [ y ] ] [dfn[x] + 1, dfn[y]] [dfn[x]+1,dfn[y]]深度最小节点的父节点。这可以用ST表来维护

代码:

package mainimport ("bufio". "fmt""math/bits""os"
)func main() {in, out := bufio.NewReader(os.Stdin), bufio.NewWriter(os.Stdout)defer out.Flush()var n, m, s, x, y intFscan(in, &n, &m, &s)g := make([][]int, n+1)for i := 0; i < n-1; i++ {Fscan(in, &x, &y)g[x] = append(g[x], y)g[y] = append(g[y], x)}t := bits.Len(uint(n))st := make([][]int, n+1) // st[i][j]表示 [i, i + 2^j)深度最小节点的父节点for i := range st {st[i] = make([]int, t)}clock := 0dfn := make([]int, n+1)var dfs func(int, int)dfs = func(u, p int) {clock++dfn[u] = clockst[clock][0] = pfor _, ne := range g[u] {if ne != p {dfs(ne, u)}}}dfs(s, 0)op := func(a, b int) int {if dfn[a] < dfn[b] {return a}return b}for j := 1; 1<<j <= n; j++ {for i := 1; i+1<<j <= n+1; i++ {st[i][j] = op(st[i][j-1], st[i+1<<(j-1)][j-1])}}lca := func(u, v int) int {if u == v {return u}if dfn[u] > dfn[v] {u, v = v, u}left := dfn[u] + 1right := dfn[v]d := bits.Len(uint(right+1-left)) - 1// [left, right], st右侧是开区间,所以下面是right+1return op(st[left][d], st[right+1-1<<d][d])}for i := 0; i < m; i++ {Fscan(in, &x, &y)Fprintln(out, lca(x, y))}
}

在这里插入图片描述

3、重链剖分

推荐观看:重链剖分
没有基础也不看上面推荐视频的话,下面代码是看不懂的

核心思路: 如果x, y不在同一个链上,那么让链头深度较小的节点向上跳。
如果在一个链上,返回深度较小的节点。

package mainimport ("bufio". "fmt""os"
)func main() {in, out := bufio.NewReader(os.Stdin), bufio.NewWriter(os.Stdout)defer out.Flush()var n, m, s, x, y intFscan(in, &n, &m, &s)g := make([][]int, n+1)for i := 0; i < n-1; i++ {Fscan(in, &x, &y)g[x] = append(g[x], y)g[y] = append(g[y], x)}son, fa, dep, sz := make([]int, n+1), make([]int, n+1), make([]int, n+1), make([]int, n+1)var dfs1 func(int, int)dfs1 = func(u, p int) {dep[u] = dep[p] + 1sz[u] = 1fa[u] = pfor _, ne := range g[u] {if ne != p {dfs1(ne, u)if sz[ne] > sz[son[u]] {son[u] = ne}sz[u] += sz[ne]}}}// todo: dfn, rank在这里用不到,这里只是为了HLD的完整性dfn, top, rank := make([]int, n+1), make([]int, n+1), make([]int, n+1)clock := 0var dfs2 func(int, int)dfs2 = func(u, tp int) {clock++dfn[u] = clocktop[u] = tprank[clock] = uif son[u] == 0 {return}dfs2(son[u], tp)for _, ne := range g[u] {if ne == fa[u] || ne == son[u] {continue}dfs2(ne, ne) // 新开一个作为链头}}dfs1(s, 0)dfs2(s, s)lca := func(x, y int) int {for top[x] != top[y] {// 保证x所在链头是深度较大的if dep[top[x]] < dep[top[y]] {x, y = y, x}x = fa[top[x]]}// 在同一条链上if dep[x] < dep[y] {return x}return y}for i := 0; i < m; i++ {Fscan(in, &x, &y)Fprintln(out, lca(x, y))}
}

运行结果:
在这里插入图片描述

相关文章:

  • UE5 游戏模板 —— TopDownGame 俯视角游戏
  • XML映射文件-辅助配置
  • Greenplum/PostgreSQL pg_hba.conf 认证方法详解
  • PCIe接口卡设计原理图:124-基于XC7Z015的PCIe低速扩展底板
  • Zephyr 高阶实践:彻底讲透 west 构建系统、模块管理与跨平台 CI/CD 配置
  • Arduino入门教程:10、屏幕显示
  • 基于SVM和dbs的孤岛检测算法
  • 如何添加项目属性表(.props)
  • TradingAgents:基于多智能体的大型语言模型(LLM)金融交易框架
  • 利用GMT绘制逐月的GRACE趋势堆叠图
  • 技术与情感交织的一生 (八)
  • 如何配置 SQL Server 混合身份验证模式​
  • RabbitMQ概念
  • 项目:Gitlab HSD CI/CD总结
  • 微信原生小程序转uniapp过程及错误总结
  • VS Code 项目中的 .vscode 目录详解
  • SSRF5 Gopher 协议对内网 Web 服务进行 sql 注入 GET 类型和POST类型
  • Jetson上的pytorch国内源下载和torchvision安装教程
  • 基于OpenCv(开源计算机视觉库)的图像旋转匹配
  • A 股无风不起浪!金融吸血科技
  • 旅社网站建设/seo是什么?
  • 唐山app开发/seo最新教程
  • 免费360地图手机版/优化关键词排名公司
  • iis网站属性里/google下载手机版
  • 自己做充值网站/重庆seo点击工具
  • 中国建设银行复核网站/新闻头条 今天